From d9480cadfcac071f69ac5ee463f778a9f3039ae1 Mon Sep 17 00:00:00 2001
From: Fabian Nack <fabiannack@web.de>
Date: Tue, 25 Nov 2014 12:02:52 +0100
Subject: [PATCH] sys - nhdp: Add nhdp impl

---
 Makefile.dep                        |   6 +
 sys/Makefile                        |   3 +
 sys/Makefile.include                |   4 +
 sys/net/routing/nhdp/Makefile       |   1 +
 sys/net/routing/nhdp/iib_table.c    | 732 ++++++++++++++++++++++++++++
 sys/net/routing/nhdp/iib_table.h    | 151 ++++++
 sys/net/routing/nhdp/lib_table.c    | 209 ++++++++
 sys/net/routing/nhdp/lib_table.h    |  89 ++++
 sys/net/routing/nhdp/nhdp.c         | 312 ++++++++++++
 sys/net/routing/nhdp/nhdp.h         | 207 ++++++++
 sys/net/routing/nhdp/nhdp_address.c | 148 ++++++
 sys/net/routing/nhdp/nhdp_address.h | 122 +++++
 sys/net/routing/nhdp/nhdp_reader.c  | 456 +++++++++++++++++
 sys/net/routing/nhdp/nhdp_reader.h  |  58 +++
 sys/net/routing/nhdp/nhdp_writer.c  | 242 +++++++++
 sys/net/routing/nhdp/nhdp_writer.h  |  77 +++
 sys/net/routing/nhdp/nib_table.c    | 328 +++++++++++++
 sys/net/routing/nhdp/nib_table.h    | 107 ++++
 18 files changed, 3252 insertions(+)
 create mode 100644 sys/net/routing/nhdp/Makefile
 create mode 100644 sys/net/routing/nhdp/iib_table.c
 create mode 100644 sys/net/routing/nhdp/iib_table.h
 create mode 100644 sys/net/routing/nhdp/lib_table.c
 create mode 100644 sys/net/routing/nhdp/lib_table.h
 create mode 100644 sys/net/routing/nhdp/nhdp.c
 create mode 100644 sys/net/routing/nhdp/nhdp.h
 create mode 100644 sys/net/routing/nhdp/nhdp_address.c
 create mode 100644 sys/net/routing/nhdp/nhdp_address.h
 create mode 100644 sys/net/routing/nhdp/nhdp_reader.c
 create mode 100644 sys/net/routing/nhdp/nhdp_reader.h
 create mode 100644 sys/net/routing/nhdp/nhdp_writer.c
 create mode 100644 sys/net/routing/nhdp/nhdp_writer.h
 create mode 100644 sys/net/routing/nhdp/nib_table.c
 create mode 100644 sys/net/routing/nhdp/nib_table.h

diff --git a/Makefile.dep b/Makefile.dep
index 730597589b..1aff898994 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -124,3 +124,9 @@ endif
 ifneq (,$(filter defaulttransceiver,$(USEMODULE)))
 	FEATURES_REQUIRED += transceiver
 endif
+
+ifneq (,$(filter nhdp,$(USEMODULE)))
+	USEMODULE += vtimer
+	USEMODULE += oonf_common
+	USEMODULE += oonf_rfc5444
+endif
diff --git a/sys/Makefile b/sys/Makefile
index 603c7725ee..b13309d055 100644
--- a/sys/Makefile
+++ b/sys/Makefile
@@ -83,6 +83,9 @@ endif
 ifneq (,$(filter trickle,$(USEMODULE)))
     DIRS += trickle
 endif
+ifneq (,$(filter nhdp,$(USEMODULE)))
+    DIRS += net/routing/nhdp
+endif
 
 DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))
 
diff --git a/sys/Makefile.include b/sys/Makefile.include
index 25e4fca899..de8b66de52 100644
--- a/sys/Makefile.include
+++ b/sys/Makefile.include
@@ -38,6 +38,10 @@ ifneq (,$(filter rpl,$(USEMODULE)))
     USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include
     USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/routing/rpl
 endif
+ifneq (,$(filter nhdp,$(USEMODULE)))
+    USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include
+    USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/routing/nhdp
+endif
 ifneq (,$(filter ieee802154,$(USEMODULE)))
     USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include
 endif
diff --git a/sys/net/routing/nhdp/Makefile b/sys/net/routing/nhdp/Makefile
new file mode 100644
index 0000000000..48422e909a
--- /dev/null
+++ b/sys/net/routing/nhdp/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/net/routing/nhdp/iib_table.c b/sys/net/routing/nhdp/iib_table.c
new file mode 100644
index 0000000000..366d24166c
--- /dev/null
+++ b/sys/net/routing/nhdp/iib_table.c
@@ -0,0 +1,732 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     nhdp
+ * @{
+ *
+ * @file
+ * @brief       Interface Information Base implementation for NHDP
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ *
+ * @}
+ */
+
+#include "mutex.h"
+#include "timex.h"
+#include "vtimer.h"
+#include "utlist.h"
+#include "kernel_types.h"
+
+#include "rfc5444/rfc5444_iana.h"
+#include "rfc5444/rfc5444_writer.h"
+
+#include "iib_table.h"
+#include "nhdp_address.h"
+#include "nhdp_writer.h"
+
+#define ENABLE_DEBUG (0)
+#include "debug.h"
+
+/* Internal variables */
+static mutex_t mtx_iib_access = MUTEX_INIT;
+static iib_base_entry_t *iib_base_entry_head = NULL;
+
+/* Internal function prototypes */
+static void rem_link_set_entry(iib_base_entry_t *base_entry, iib_link_set_entry_t *ls_entry);
+static void cleanup_link_sets(nhdp_addr_entry_t *rem_list);
+static iib_link_set_entry_t *add_default_link_set_entry(iib_base_entry_t *base_entry, timex_t *now,
+                                                        uint64_t val_time);
+static void reset_link_set_entry(iib_link_set_entry_t *ls_entry, timex_t *now, uint64_t val_time);
+static iib_link_set_entry_t *update_link_set(iib_base_entry_t *base_entry, nib_entry_t *nb_elt,
+                                             nhdp_addr_entry_t *send_list, timex_t *now,
+                                             uint64_t val_time, uint8_t sym, uint8_t lost);
+static void release_link_tuple_addresses(iib_link_set_entry_t *ls_entry);
+
+static int update_two_hop_set(iib_base_entry_t *base_entry, iib_link_set_entry_t *ls_entry,
+                              nhdp_addr_entry_t *th_sym_list, nhdp_addr_entry_t *th_rem_list,
+                              timex_t *now, uint64_t val_time);
+static uint8_t rem_exst_th_entries(iib_base_entry_t *base_entry, iib_two_hop_set_entry_t *ts_elt,
+                                   nhdp_addr_entry_t *th_sym_list);
+static uint8_t rem_non_sym_th_entries(iib_base_entry_t *base_entry,
+                                      iib_two_hop_set_entry_t *ts_elt,
+                                      nhdp_addr_entry_t *th_rem_list);
+static int add_two_hop_entry(iib_base_entry_t *base_entry, iib_link_set_entry_t *ls_entry,
+                             nhdp_addr_t *th_addr, timex_t *now, uint64_t val_time);
+static void rem_two_hop_entry(iib_base_entry_t *base_entry, iib_two_hop_set_entry_t *th_entry);
+
+static void wr_update_ls_status(iib_base_entry_t *base_entry,
+                                iib_link_set_entry_t *ls_elt, timex_t *now);
+static void update_nb_tuple_symmetry(iib_base_entry_t *base_entry,
+                                     iib_link_set_entry_t *ls_entry, timex_t *now);
+static void rem_not_heard_nb_tuple(iib_link_set_entry_t *ls_entry, timex_t *now);
+
+static inline timex_t get_max_timex(timex_t time_one, timex_t time_two);
+static iib_link_tuple_status_t get_tuple_status(iib_link_set_entry_t *ls_entry, timex_t *now);
+
+
+/*---------------------------------------------------------------------------*
+ *                       Interface Information Base API                      *
+ *---------------------------------------------------------------------------*/
+
+int iib_register_if(kernel_pid_t pid)
+{
+    iib_base_entry_t *new_entry = (iib_base_entry_t *) malloc(sizeof(iib_base_entry_t));
+
+    if (!new_entry) {
+        /* Insufficient memory */
+        return -1;
+    }
+
+    new_entry->if_pid = pid;
+    new_entry->link_set_head = NULL;
+    new_entry->two_hop_set_head = NULL;
+    LL_PREPEND(iib_base_entry_head, new_entry);
+
+    return 0;
+}
+
+int iib_process_hello(kernel_pid_t if_pid, nib_entry_t *nb_elt, nhdp_addr_entry_t *send_list,
+                      nhdp_addr_entry_t *th_sym_list, nhdp_addr_entry_t *th_rem_list,
+                      nhdp_addr_entry_t *rem_list, uint64_t validity_time, uint8_t is_sym_nb,
+                      uint8_t is_lost)
+{
+    iib_base_entry_t *base_elt;
+    timex_t now;
+
+    mutex_lock(&mtx_iib_access);
+
+    /* Remove link tuple addresses that are included in the Removed Addr List */
+    cleanup_link_sets(rem_list);
+
+    LL_FOREACH(iib_base_entry_head, base_elt) {
+        /* Find the link set and two hop set for the interface */
+        if (base_elt->if_pid == if_pid) {
+            break;
+        }
+    }
+
+    if (base_elt) {
+        vtimer_now(&now);
+
+        /* Create a new link tuple for the neighbor that originated the hello */
+        iib_link_set_entry_t *ls_entry = update_link_set(base_elt, nb_elt, send_list,
+                                         &now, validity_time, is_sym_nb, is_lost);
+
+        /* Create new two hop tuples for signaled symmetric neighbors */
+        if (ls_entry) {
+            update_two_hop_set(base_elt, ls_entry, th_sym_list, th_rem_list, &now, validity_time);
+        }
+    }
+
+    mutex_unlock(&mtx_iib_access);
+
+    return 0;
+}
+
+void iib_fill_wr_addresses(kernel_pid_t if_pid, struct rfc5444_writer *wr)
+{
+    iib_base_entry_t *base_elt;
+    iib_link_set_entry_t *ls_elt;
+    nhdp_addr_entry_t *addr_elt;
+    timex_t now;
+
+    mutex_lock(&mtx_iib_access);
+
+    vtimer_now(&now);
+
+    /* Before adding addresses first update the status of all link tuples */
+    iib_update_lt_status(&now);
+
+    /* Add all addresses of Link Tuples of the given interface's Link Set to the current HELLO */
+    LL_FOREACH(iib_base_entry_head, base_elt) {
+        if (base_elt->if_pid == if_pid) {
+            LL_FOREACH(base_elt->link_set_head, ls_elt) {
+                if (ls_elt->last_status != IIB_LT_STATUS_PENDING) {
+                    /* Exclude addresses from tuples with L_STATUS = PENDING */
+                    LL_FOREACH(ls_elt->address_list_head, addr_elt) {
+                        if (!NHDP_ADDR_TMP_IN_ANY(addr_elt->address)) {
+                            /* Add address to the writers next packet */
+                            switch (ls_elt->last_status) {
+                                case IIB_LT_STATUS_SYM:
+                                    nhdp_writer_add_addr(wr, addr_elt->address,
+                                                         RFC5444_ADDRTLV_LINK_STATUS,
+                                                         RFC5444_LINKSTATUS_SYMMETRIC);
+                                    addr_elt->address->in_tmp_table = NHDP_ADDR_TMP_SYM;
+                                    break;
+
+                                case IIB_LT_STATUS_HEARD:
+                                    nhdp_writer_add_addr(wr, addr_elt->address,
+                                                         RFC5444_ADDRTLV_LINK_STATUS,
+                                                         RFC5444_LINKSTATUS_HEARD);
+                                    addr_elt->address->in_tmp_table = NHDP_ADDR_TMP_ANY;
+                                    break;
+
+                                case IIB_LT_STATUS_UNKNOWN:
+                                    /* Fall through */
+
+                                case IIB_LT_STATUS_LOST:
+                                    nhdp_writer_add_addr(wr, addr_elt->address,
+                                                         RFC5444_ADDRTLV_LINK_STATUS,
+                                                         RFC5444_LINKSTATUS_LOST);
+                                    addr_elt->address->in_tmp_table = NHDP_ADDR_TMP_ANY;
+                                    break;
+
+                                case IIB_LT_STATUS_PENDING:
+                                    /* Pending link tuples are not included */
+                                    break;
+
+                                default:
+                                    /* Should not happen */
+                                    DEBUGF("[WARNING] Unknown link tuple status\n");
+                                    break;
+                            }
+                        }
+                    }
+                }
+            }
+            /* IF's link set found */
+            break;
+        }
+    }
+
+    mutex_unlock(&mtx_iib_access);
+}
+
+void iib_update_lt_status(timex_t *now)
+{
+    iib_base_entry_t *base_elt;
+    iib_link_set_entry_t *ls_elt, *ls_tmp;
+
+    LL_FOREACH(iib_base_entry_head, base_elt) {
+        LL_FOREACH_SAFE(base_elt->link_set_head, ls_elt, ls_tmp) {
+            wr_update_ls_status(base_elt, ls_elt, now);
+        }
+    }
+}
+
+void iib_propagate_nb_entry_change(nib_entry_t *old_entry, nib_entry_t *new_entry)
+{
+    iib_base_entry_t *base_elt;
+    iib_link_set_entry_t *ls_elt;
+    LL_FOREACH(iib_base_entry_head, base_elt) {
+        LL_FOREACH(base_elt->link_set_head, ls_elt) {
+            if (ls_elt->nb_elt == old_entry) {
+                ls_elt->nb_elt = new_entry;
+            }
+        }
+    }
+}
+
+
+/*------------------------------------------------------------------------------------*/
+/*                                Internal functions                                  */
+/*------------------------------------------------------------------------------------*/
+
+/**
+ * Remove addresses included in the Removed Address List from all existing Link Tuples
+ */
+static void cleanup_link_sets(nhdp_addr_entry_t *rem_list)
+{
+    /* Check whether the Removed Address List is not empty */
+    if (!rem_list) {
+        return;
+    }
+
+    /* Loop through all link sets */
+    iib_base_entry_t *base_elt;
+    LL_FOREACH(iib_base_entry_head, base_elt) {
+        /* Loop through all link tuples of the link set */
+        iib_link_set_entry_t *ls_elt, *ls_tmp;
+        LL_FOREACH_SAFE(base_elt->link_set_head, ls_elt, ls_tmp) {
+            /* Loop through all addresses of the link tuples */
+            nhdp_addr_entry_t *lt_elt;
+            LL_FOREACH(ls_elt->address_list_head, lt_elt) {
+                /* Loop through all addresses of the Removed Addr List */
+                nhdp_addr_entry_t *rem_elt;
+                LL_FOREACH(rem_list, rem_elt) {
+                    /* Remove link tuple address if included in the Removed Addr List */
+                    if (lt_elt->address == rem_elt->address) {
+                        /* Addresses are equal (same NHDP address db entry) */
+                        LL_DELETE(ls_elt->address_list_head, lt_elt);
+                        nhdp_free_addr_entry(lt_elt);
+                        break;
+                    }
+                }
+            }
+
+            /* Remove link tuples with empty address list */
+            if (!ls_elt->address_list_head) {
+                if (ls_elt->last_status == IIB_LT_STATUS_SYM) {
+                    /* Remove all two hop entries for the corresponding link tuple */
+                    iib_two_hop_set_entry_t *th_elt, *th_tmp;
+                    LL_FOREACH_SAFE(base_elt->two_hop_set_head, th_elt, th_tmp) {
+                        if (th_elt->ls_elt == ls_elt) {
+                            rem_two_hop_entry(base_elt, th_elt);
+                        }
+                    }
+                }
+
+                rem_link_set_entry(base_elt, ls_elt);
+            }
+        }
+    }
+}
+
+/**
+ * Update the Link Set for the receiving interface during HELLO message processing
+ */
+static iib_link_set_entry_t *update_link_set(iib_base_entry_t *base_entry, nib_entry_t *nb_elt,
+                                             nhdp_addr_entry_t *send_list, timex_t *now,
+                                             uint64_t val_time, uint8_t sym, uint8_t lost)
+{
+    iib_link_set_entry_t *ls_elt, *ls_tmp;
+    iib_link_set_entry_t *matching_lt = NULL;
+    nhdp_addr_entry_t *lt_elt, *send_elt;
+    timex_t v_time, l_hold;
+    uint8_t matches = 0;
+
+    /* Loop through every link tuple of the interface to update the link set */
+    LL_FOREACH_SAFE(base_entry->link_set_head, ls_elt, ls_tmp) {
+        /* Loop through all addresses of the link tuple */
+        LL_FOREACH(ls_elt->address_list_head, lt_elt) {
+            /* Loop through all addresses of the Sending Addr List */
+            LL_FOREACH(send_list, send_elt) {
+                /* If link tuple address matches a sending addr we found a fitting tuple */
+                if (lt_elt->address == send_elt->address) {
+                    /* Addresses are equal (same NHDP address db entry) */
+                    matches++;
+
+                    if (matches > 1) {
+                        /* Multiple matching link tuples, delete the previous one */
+                        if (matching_lt->last_status == IIB_LT_STATUS_SYM) {
+                            update_nb_tuple_symmetry(base_entry, matching_lt, now);
+                        }
+
+                        rem_link_set_entry(base_entry, matching_lt);
+                    }
+
+                    matching_lt = ls_elt;
+                    break;
+                }
+
+                if (matching_lt == ls_elt) {
+                    /* This link tuple is already detected as matching */
+                    break;
+                }
+            }
+        }
+    }
+
+    if (matches > 1) {
+        /* Multiple matching link tuples, reset the last one for reuse */
+        if (matching_lt->last_status == IIB_LT_STATUS_SYM) {
+            update_nb_tuple_symmetry(base_entry, matching_lt, now);
+        }
+
+        reset_link_set_entry(matching_lt, now, val_time);
+    }
+    else if (matches == 1) {
+        /* A single matching link tuple, only release the address list */
+        release_link_tuple_addresses(matching_lt);
+    }
+    else {
+        /* No single matching link tuple existant, create a new one */
+        matching_lt = add_default_link_set_entry(base_entry, now, val_time);
+
+        if (!matching_lt) {
+            /* Insufficient memory */
+            return NULL;
+        }
+    }
+
+    v_time = timex_from_uint64(val_time * MS_IN_USEC);
+    l_hold = timex_from_uint64(((uint64_t)NHDP_L_HOLD_TIME_MS) * MS_IN_USEC);
+
+    /* Set Sending Address List as this tuples address list */
+    matching_lt->address_list_head = nhdp_generate_new_addr_list(send_list);
+
+    if (!matching_lt->address_list_head) {
+        /* Insufficient memory */
+        rem_link_set_entry(base_entry, matching_lt);
+        return NULL;
+    }
+
+    matching_lt->nb_elt = nb_elt;
+
+    /* Set values dependent on link status */
+    if (sym) {
+        if (matching_lt->last_status != IIB_LT_STATUS_SYM) {
+            /* Set corresponding neighbor tuple to symmetric (Section 13.1 of RFC 6130) */
+            if (matching_lt->nb_elt) {
+                nib_set_nb_entry_sym(matching_lt->nb_elt);
+            }
+        }
+
+        matching_lt->sym_time = timex_add(*now, v_time);
+        matching_lt->last_status = IIB_LT_STATUS_SYM;
+    }
+    else if (lost) {
+        matching_lt->sym_time.microseconds = 0;
+        matching_lt->sym_time.seconds = 0;
+
+        if (matching_lt->last_status == IIB_LT_STATUS_SYM) {
+            update_nb_tuple_symmetry(base_entry, matching_lt, now);
+        }
+
+        if (get_tuple_status(matching_lt, now) == IIB_LT_STATUS_HEARD) {
+            matching_lt->last_status = IIB_LT_STATUS_HEARD;
+            matching_lt->exp_time = timex_add(*now, l_hold);
+        }
+        else {
+            matching_lt->last_status = IIB_LT_STATUS_UNKNOWN;
+        }
+    }
+
+    /* Set time values */
+    matching_lt->heard_time = get_max_timex(timex_add(*now, v_time), matching_lt->sym_time);
+
+    if (matching_lt->pending) {
+        /* L_status is PENDING */
+        matching_lt->exp_time = get_max_timex(matching_lt->exp_time, matching_lt->heard_time);
+    }
+    else if (!matching_lt->lost) {
+        if ((timex_cmp(matching_lt->sym_time, *now) == 1)
+            || (timex_cmp(matching_lt->heard_time, *now) == 1)) {
+            /* L_status is HEARD or SYMMETRIC */
+            matching_lt->exp_time = get_max_timex(matching_lt->exp_time,
+                                                  timex_add(matching_lt->heard_time, l_hold));
+        }
+    }
+
+    return matching_lt;
+}
+
+/**
+ * Update the status of a link tuple and process necessary changes and execute
+ * necessary changes in the 2-Hop Set and in the Neighbor Information Base
+ * Implements logic of Section 13 of RFC 6130
+ */
+static void wr_update_ls_status(iib_base_entry_t *base_entry,
+                                iib_link_set_entry_t *ls_elt, timex_t *now)
+{
+    if (timex_cmp(ls_elt->exp_time, *now) != 1) {
+        /* Entry expired and has to be removed */
+        if (ls_elt->last_status == IIB_LT_STATUS_SYM) {
+            update_nb_tuple_symmetry(base_entry, ls_elt, now);
+        }
+
+        rem_not_heard_nb_tuple(ls_elt, now);
+        rem_link_set_entry(base_entry, ls_elt);
+    }
+    else if ((ls_elt->last_status == IIB_LT_STATUS_SYM)
+             && (timex_cmp(ls_elt->sym_time, *now) != 1)) {
+        /* Status changed from SYMMETRIC to HEARD */
+        update_nb_tuple_symmetry(base_entry, ls_elt, now);
+        ls_elt->last_status = IIB_LT_STATUS_HEARD;
+
+        if (timex_cmp(ls_elt->heard_time, *now) != 1) {
+            /* New status is LOST (equals IIB_LT_STATUS_UNKNOWN) */
+            rem_not_heard_nb_tuple(ls_elt, now);
+            ls_elt->nb_elt = NULL;
+            ls_elt->last_status = IIB_LT_STATUS_UNKNOWN;
+        }
+    }
+    else if ((ls_elt->last_status == IIB_LT_STATUS_HEARD)
+             && (timex_cmp(ls_elt->heard_time, *now) != 1)) {
+        /* Status changed from HEARD to LOST (equals IIB_LT_STATUS_UNKNOWN) */
+        rem_not_heard_nb_tuple(ls_elt, now);
+        ls_elt->nb_elt = NULL;
+        ls_elt->last_status = IIB_LT_STATUS_UNKNOWN;
+    }
+}
+
+/**
+ * Add a new Link Tuple with default values to the given Link Set
+ */
+static iib_link_set_entry_t *add_default_link_set_entry(iib_base_entry_t *base_entry, timex_t *now,
+                                                        uint64_t val_time)
+{
+    iib_link_set_entry_t *new_entry;
+    timex_t v_time = timex_from_uint64(val_time * MS_IN_USEC);
+
+    new_entry = (iib_link_set_entry_t *) malloc(sizeof(iib_link_set_entry_t));
+
+    if (!new_entry) {
+        /* Insufficient memory */
+        return NULL;
+    }
+
+    new_entry->address_list_head = NULL;
+    new_entry->heard_time.microseconds = 0;
+    new_entry->heard_time.seconds = 0;
+    new_entry->sym_time.microseconds = 0;
+    new_entry->sym_time.seconds = 0;
+    new_entry->pending = NHDP_INITIAL_PENDING;
+    new_entry->lost = 0;
+    new_entry->exp_time = timex_add(*now, v_time);
+    new_entry->last_status = IIB_LT_STATUS_UNKNOWN;
+    new_entry->nb_elt = NULL;
+    LL_PREPEND(base_entry->link_set_head, new_entry);
+
+    return new_entry;
+}
+
+/**
+ * Reset a given Link Tuple for reusage
+ */
+static void reset_link_set_entry(iib_link_set_entry_t *ls_entry, timex_t *now, uint64_t val_time)
+{
+    timex_t v_time = timex_from_uint64(val_time * MS_IN_USEC);
+
+    release_link_tuple_addresses(ls_entry);
+    ls_entry->sym_time.microseconds = 0;
+    ls_entry->sym_time.seconds = 0;
+    ls_entry->heard_time.microseconds = 0;
+    ls_entry->heard_time.seconds = 0;
+    ls_entry->pending = NHDP_INITIAL_PENDING;
+    ls_entry->lost = 0;
+    ls_entry->exp_time = timex_add(*now, v_time);
+    ls_entry->nb_elt = NULL;
+    ls_entry->last_status = IIB_LT_STATUS_UNKNOWN;
+}
+
+/**
+ * Remove a given Link Tuple
+ */
+static void rem_link_set_entry(iib_base_entry_t *base_entry, iib_link_set_entry_t *ls_entry)
+{
+    LL_DELETE(base_entry->link_set_head, ls_entry);
+    release_link_tuple_addresses(ls_entry);
+    free(ls_entry);
+}
+
+/**
+ * Free all address entries of a link tuple
+ */
+static void release_link_tuple_addresses(iib_link_set_entry_t *ls_entry)
+{
+    nhdp_free_addr_list(ls_entry->address_list_head);
+    ls_entry->address_list_head = NULL;
+}
+
+/**
+ * Update the 2-Hop Set during HELLO message processing
+ */
+static int update_two_hop_set(iib_base_entry_t *base_entry, iib_link_set_entry_t *ls_entry,
+                              nhdp_addr_entry_t *th_sym_list, nhdp_addr_entry_t *th_rem_list,
+                              timex_t *now, uint64_t val_time)
+{
+    /* Check whether a corresponding link tuple was created */
+    if (ls_entry == NULL) {
+        return -1;
+    }
+
+    /* If the link to the neighbor is still symmetric */
+    if (get_tuple_status(ls_entry, now) == IIB_LT_STATUS_SYM) {
+        iib_two_hop_set_entry_t *ths_elt, *ths_tmp;
+        nhdp_addr_entry_t *sym_elt;
+
+        /* Loop through all the two hop tuples of the two hop set */
+        LL_FOREACH_SAFE(base_entry->two_hop_set_head, ths_elt, ths_tmp) {
+            if (timex_cmp(ths_elt->exp_time, *now) != 1) {
+                /* Entry is expired, remove it */
+                rem_two_hop_entry(base_entry, ths_elt);
+            }
+            else if (ths_elt->ls_elt == ls_entry) {
+                if (!rem_non_sym_th_entries(base_entry, ths_elt, th_rem_list)) {
+                    rem_exst_th_entries(base_entry, ths_elt, th_sym_list);
+                }
+            }
+        }
+
+        /* Add a new entry for every signaled symmetric neighbor address */
+        LL_FOREACH(th_sym_list, sym_elt) {
+            if (add_two_hop_entry(base_entry, ls_entry, sym_elt->address, now, val_time)) {
+                /* No more memory available, return error */
+                return -1;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * Remove an existing 2-Hop Tuple if its address was signalled as lost in the received HELLO
+ */
+static uint8_t rem_non_sym_th_entries(iib_base_entry_t *base_entry,
+                                      iib_two_hop_set_entry_t *ts_elt,
+                                      nhdp_addr_entry_t *th_rem_list)
+{
+    nhdp_addr_entry_t *rem_elt;
+
+    /* Remove the given two hop entry if it was signaled as lost */
+    LL_FOREACH(th_rem_list, rem_elt) {
+        if (ts_elt->th_nb_addr == rem_elt->address) {
+            /* Addresses are equal (same NHDP address db entry) */
+            rem_two_hop_entry(base_entry, ts_elt);
+            return 1;
+        }
+    }
+    return 0;
+}
+
+/**
+ * Remove an existing 2-Hop Tuple if it is present in the 2-Hop symmetric address
+ * list of the received HELLO
+ */
+static uint8_t rem_exst_th_entries(iib_base_entry_t *base_entry,
+                                   iib_two_hop_set_entry_t *ts_elt, nhdp_addr_entry_t *th_sym_list)
+{
+    nhdp_addr_entry_t *sym_elt;
+
+    /* Remove the given two hop entry if it was signaled as symmetric (new one is added) */
+    LL_FOREACH(th_sym_list, sym_elt) {
+        if (ts_elt->th_nb_addr == sym_elt->address) {
+            /* Addresses are equal (same NHDP address db entry) */
+            rem_two_hop_entry(base_entry, ts_elt);
+            return 1;
+        }
+    }
+    return 0;
+}
+
+/**
+ * Add a 2-Hop Tuple for a given address
+ */
+static int add_two_hop_entry(iib_base_entry_t *base_entry, iib_link_set_entry_t *ls_entry,
+                             nhdp_addr_t *th_addr, timex_t *now, uint64_t val_time)
+{
+    iib_two_hop_set_entry_t *new_entry;
+    timex_t v_time = timex_from_uint64(val_time * MS_IN_USEC);
+
+    new_entry = (iib_two_hop_set_entry_t *) malloc(sizeof(iib_two_hop_set_entry_t));
+
+    if (!new_entry) {
+        /* Insufficient memory */
+        return -1;
+    }
+
+    /* Increment usage counter of address in central NHDP address storage */
+    th_addr->usg_count++;
+    new_entry->th_nb_addr = th_addr;
+    new_entry->ls_elt = ls_entry;
+    new_entry->exp_time = timex_add(*now, v_time);
+    LL_PREPEND(base_entry->two_hop_set_head, new_entry);
+
+    return 0;
+}
+
+/**
+ * Remove a given 2-Hop Tuple
+ */
+static void rem_two_hop_entry(iib_base_entry_t *base_entry, iib_two_hop_set_entry_t *th_entry)
+{
+    LL_DELETE(base_entry->two_hop_set_head, th_entry);
+    nhdp_decrement_addr_usage(th_entry->th_nb_addr);
+    free(th_entry);
+}
+
+/**
+ * Remove all corresponding two hop entries for a given link tuple that lost symmetry status.
+ * Additionally reset the neighbor tuple's symmmetry flag (for the neighbor tuple this link
+ * tuple is represented in), if no more corresponding symmetric link tuples are left.
+ * Implements section 13.2 of RFC 6130
+ */
+static void update_nb_tuple_symmetry(iib_base_entry_t *base_entry,
+                                     iib_link_set_entry_t *ls_entry, timex_t *now)
+{
+    iib_two_hop_set_entry_t *th_elt, *th_tmp;
+
+    /* First remove all two hop entries for the corresponding link tuple */
+    LL_FOREACH_SAFE(base_entry->two_hop_set_head, th_elt, th_tmp) {
+        if (th_elt->ls_elt == ls_entry) {
+            rem_two_hop_entry(base_entry, th_elt);
+        }
+    }
+
+    /* Afterwards check the neighbor tuple containing the link tuple's addresses */
+    if ((ls_entry->nb_elt != NULL) && (ls_entry->nb_elt->symmetric == 1)) {
+        iib_base_entry_t *base_tmp;
+        LL_FOREACH(iib_base_entry_head, base_tmp) {
+            iib_link_set_entry_t *ls_tmp;
+            LL_FOREACH(base_tmp->link_set_head, ls_tmp) {
+                if ((ls_entry->nb_elt == ls_tmp->nb_elt) && (ls_entry != ls_tmp)) {
+                    if (timex_cmp(ls_tmp->sym_time, *now) == 1) {
+                        return;
+                    }
+                }
+            }
+        }
+
+        /* No remaining symmetric link tuple for the neighbor tuple */
+        nib_reset_nb_entry_sym(ls_entry->nb_elt, now);
+    }
+}
+
+/**
+ * Remove a neighbor tuple if no more corresponding heard link tuples are left
+ * Implements section 13.3 of RFC 6130
+ */
+static void rem_not_heard_nb_tuple(iib_link_set_entry_t *ls_entry, timex_t *now)
+{
+    /* Check whether the corresponding neighbor tuple still exists */
+    if (ls_entry->nb_elt) {
+        iib_base_entry_t *base_tmp;
+        LL_FOREACH(iib_base_entry_head, base_tmp) {
+            iib_link_set_entry_t *ls_tmp;
+            LL_FOREACH(base_tmp->link_set_head, ls_tmp) {
+                if ((ls_entry->nb_elt == ls_tmp->nb_elt) && (ls_entry != ls_tmp)) {
+                    if (timex_cmp(ls_tmp->heard_time, *now) == 1) {
+                        return;
+                    }
+
+                    ls_tmp->nb_elt = NULL;
+                }
+            }
+        }
+
+        /* No remaining heard link tuple for the neighbor tuple */
+        nib_rem_nb_entry(ls_entry->nb_elt);
+    }
+}
+
+/**
+ * Get the L_STATUS value of a given link tuple
+ */
+static iib_link_tuple_status_t get_tuple_status(iib_link_set_entry_t *ls_entry, timex_t *now)
+{
+    if (ls_entry->pending) {
+        return IIB_LT_STATUS_PENDING;
+    }
+    else if (ls_entry->lost) {
+        return IIB_LT_STATUS_LOST;
+    }
+    else if (timex_cmp(ls_entry->sym_time, *now) == 1) {
+        return IIB_LT_STATUS_SYM;
+    }
+    else if (timex_cmp(ls_entry->heard_time, *now) == 1) {
+        return IIB_LT_STATUS_HEARD;
+    }
+
+    return IIB_LT_STATUS_UNKNOWN;
+}
+
+/**
+ * Get the later one of two timex representation
+ */
+static inline timex_t get_max_timex(timex_t time_one, timex_t time_two)
+{
+    if (timex_cmp(time_one, time_two) != -1) {
+        return time_one;
+    }
+
+    return time_two;
+}
diff --git a/sys/net/routing/nhdp/iib_table.h b/sys/net/routing/nhdp/iib_table.h
new file mode 100644
index 0000000000..77e63ff8d7
--- /dev/null
+++ b/sys/net/routing/nhdp/iib_table.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     nhdp
+ * @{
+ *
+ * @file
+ * @brief       Interface Information Base interface for NHDP
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ */
+
+#ifndef IIB_TABLE_H_
+#define IIB_TABLE_H_
+
+#include "timex.h"
+#include "kernel_types.h"
+
+#include "nib_table.h"
+#include "nhdp_address.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Possible L_STATUS values of a link tuple
+ */
+typedef enum iib_link_tuple_status_t {
+    IIB_LT_STATUS_PENDING,
+    IIB_LT_STATUS_LOST,
+    IIB_LT_STATUS_HEARD,
+    IIB_LT_STATUS_SYM,
+    IIB_LT_STATUS_UNKNOWN
+} iib_link_tuple_status_t;
+
+/**
+ * @brief   Link Set entry (link tuple)
+ */
+typedef struct iib_link_set_entry_t {
+    nhdp_addr_entry_t *address_list_head;       /**< Pointer to head of this tuple's addresses */
+    timex_t heard_time;                         /**< Time at which entry leaves heard status */
+    timex_t sym_time;                           /**< Time at which entry leaves symmetry status */
+    uint8_t pending;                            /**< Flag whether link is pending */
+    uint8_t lost;                               /**< Flag whether link is lost */
+    timex_t exp_time;                           /**< Time at which entry expires */
+    nib_entry_t *nb_elt;                        /**< Pointer to corresponding nb tuple */
+    enum iib_link_tuple_status_t last_status;   /**< Last processed status of link tuple */
+    struct iib_link_set_entry_t *next;          /**< Pointer to next list entry */
+} iib_link_set_entry_t;
+
+/**
+ * @brief   2-Hop Set entry (2-Hop tuple)
+ */
+typedef struct iib_two_hop_set_entry_t {
+    struct iib_link_set_entry_t *ls_elt;        /**< Pointer to corresponding link tuple */
+    nhdp_addr_t *th_nb_addr;                    /**< Address of symmetric 2-hop neighbor */
+    timex_t exp_time;                           /**< Time at which entry expires */
+    struct iib_two_hop_set_entry_t *next;       /**< Pointer to next list entry */
+} iib_two_hop_set_entry_t;
+
+/**
+ * @brief   Link set for a registered interface
+ */
+typedef struct iib_base_entry_t {
+    kernel_pid_t if_pid;                                /**< PID of the interface */
+    struct iib_link_set_entry_t *link_set_head;         /**< Pointer to this if's link tuples */
+    struct iib_two_hop_set_entry_t *two_hop_set_head;   /**< Pointer to this if's 2-hop tuples */
+    struct iib_base_entry_t *next;                      /**< Pointer to next list entry */
+} iib_base_entry_t;
+
+/**
+ * @brief                   Register a new interface in the IIB
+ *
+ * This function creates a new empty Link Set and a new empty 2-Hop Set for the
+ * given interface.
+ *
+ * @param[in] pid           PID of the interface
+ *
+ * @return                  0 on success
+ * @return                  -1 on error
+ */
+int iib_register_if(kernel_pid_t pid);
+
+/**
+ * @brief                   Process a received HELLO message in the IIB
+ *
+ * @note
+ * Must not be called from outside the NHDP reader's message processing.
+ *
+ * @param[in] if_pid        PID of the interface the message was received on
+ * @param[in] nb_elt        Pointer to the Neighbor Tuple for the message originator
+ * @param[in] send_list     Pointer to the Sending Address List from the received HELLO
+ * @param[in] th_sym_list   Pointer to the Addr List of the originator's symmetric 2-Hop neighbors
+ * @param[in] th_rem_list   Pointer to the Addr List of the originator's lost 2-Hop neighbors
+ * @param[in] rem_list      Pointer to the Removed Address List
+ * @param[in] validity_time Validity time in milliseconds for the originator's information
+ * @param[in] is_sym_nb     Flag whether the link to the originator is symmetric
+ * @param[in] is_lost       Flag whether the originator marked this link as lost
+ *
+ * @return                  0 on success
+ */
+int iib_process_hello(kernel_pid_t if_pid, nib_entry_t *nb_elt, nhdp_addr_entry_t *send_list,
+                      nhdp_addr_entry_t *th_sym_list, nhdp_addr_entry_t *th_rem_list,
+                      nhdp_addr_entry_t *rem_list, uint64_t validity_time, uint8_t is_sym_nb,
+                      uint8_t is_lost);
+
+/**
+ * @brief                   Add addresses to the currently constructed HELLO message
+ *
+ * @note
+ * Must not be called from outside the NHDP writer's message creation process.
+ *
+ * @param[in] if_pid        PID of the interface the message is constructed for
+ * @param[in] wr            The NHDP writer used for message construction
+ */
+void iib_fill_wr_addresses(kernel_pid_t if_pid, struct rfc5444_writer *wr);
+
+/**
+ * @brief                   Update L_STATUS of all existing Link Tuples
+ *
+ * @note
+ * If a status change appears the steps described in section 13 of RFC 6130 are executed.
+ *
+ * @param[in] now           Pointer to current time timex representation
+ */
+void iib_update_lt_status(timex_t *now);
+
+/**
+ * @brief                   Exchange the corresponding Neighbor Tuple of existing Link Tuples
+ *
+ * This function exchanges the corresponding Neighbor Tuple of every Link Tuple that
+ * was assigned to old_entry. Primarily used on Neighbor Tuple deletion of old_entry.
+ *
+ * @param[in] old_entry     Pointer to the old corresponding Neighbor Tuple
+ * @param[in] new_entry     Pointer to the new corresponding Neighbor Tuple
+ */
+void iib_propagate_nb_entry_change(nib_entry_t *old_entry, nib_entry_t *new_entry);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IIB_TABLE_H_ */
+/** @} */
diff --git a/sys/net/routing/nhdp/lib_table.c b/sys/net/routing/nhdp/lib_table.c
new file mode 100644
index 0000000000..c35e1c684c
--- /dev/null
+++ b/sys/net/routing/nhdp/lib_table.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     nhdp
+ * @{
+ *
+ * @file
+ * @brief       Local Information Base implementation for NHDP
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ *
+ * @}
+ */
+
+#include "mutex.h"
+#include "utlist.h"
+#include "kernel_types.h"
+
+#include "rfc5444/rfc5444_iana.h"
+#include "rfc5444/rfc5444_writer.h"
+
+#include "lib_table.h"
+#include "nhdp_address.h"
+#include "nhdp_writer.h"
+
+/* Internal variables */
+static mutex_t mtx_lib_access = MUTEX_INIT;
+static lib_entry_t *lib_entry_head = NULL;
+
+/* Internal function prototypes */
+static int create_if_entry(kernel_pid_t if_pid, nhdp_addr_t *addr);
+static int add_address_to_if(lib_entry_t *if_entry, nhdp_addr_t *addr);
+
+
+/*---------------------------------------------------------------------------*
+ *                       Local Information Base API                          *
+ *---------------------------------------------------------------------------*/
+
+int lib_add_if_addr(kernel_pid_t if_pid, nhdp_addr_t *addr)
+{
+    lib_entry_t *lib_elt;
+    nhdp_addr_entry_t *addr_elt;
+    int result = -1;
+
+    mutex_lock(&mtx_lib_access);
+
+    /* Check whether the given interface is already registered */
+    LL_FOREACH(lib_entry_head, lib_elt) {
+        if (lib_elt->if_pid == if_pid) {
+            LL_FOREACH(lib_entry_head->if_addr_list_head, addr_elt) {
+                if (addr_elt->address == addr) {
+                    /* Address already known for the interface */
+                    result = 0;
+                    break;
+                }
+            }
+
+            if (result) {
+                /* Existing interface entry, but new address */
+                result = add_address_to_if(lib_elt, addr);
+                break;
+            }
+        }
+    }
+
+    if (result) {
+        /* New interface, create a lib entry */
+        result = create_if_entry(if_pid, addr);
+    }
+
+    mutex_unlock(&mtx_lib_access);
+
+    return result;
+}
+
+void lib_rem_if(kernel_pid_t if_pid)
+{
+    lib_entry_t *lib_elt, *lib_tmp;
+
+    mutex_lock(&mtx_lib_access);
+
+    LL_FOREACH_SAFE(lib_entry_head, lib_elt, lib_tmp) {
+        if (lib_elt->if_pid == if_pid) {
+            nhdp_free_addr_list(lib_elt->if_addr_list_head);
+            LL_DELETE(lib_entry_head, lib_elt);
+            free(lib_elt);
+            break;
+        }
+    }
+
+    mutex_unlock(&mtx_lib_access);
+}
+
+void lib_fill_wr_addresses(kernel_pid_t if_pid, struct rfc5444_writer *wr)
+{
+    lib_entry_t *lib_elt;
+    nhdp_addr_entry_t *add_tmp;
+
+    mutex_lock(&mtx_lib_access);
+
+    /* First fill the list for LOCAL_IF = THIS_IF */
+    LL_FOREACH(lib_entry_head, lib_elt) {
+        if (lib_elt->if_pid == if_pid) {
+            LL_FOREACH(lib_elt->if_addr_list_head, add_tmp) {
+                nhdp_writer_add_addr(wr, add_tmp->address,
+                                     RFC5444_ADDRTLV_LOCAL_IF, RFC5444_LOCALIF_THIS_IF);
+                add_tmp->address->in_tmp_table = NHDP_ADDR_TMP_ANY;
+            }
+            break;
+        }
+    }
+
+    /* Second fill the list for LOCAL_IF = OTHER_IF */
+    LL_FOREACH(lib_entry_head, lib_elt) {
+        if (lib_elt->if_pid != if_pid) {
+            LL_FOREACH(lib_elt->if_addr_list_head, add_tmp) {
+                /* Check if this address is not already included in a list */
+                if (!NHDP_ADDR_TMP_IN_ANY(add_tmp->address)) {
+                    /* Address can be added */
+                    nhdp_writer_add_addr(wr, add_tmp->address,
+                                         RFC5444_ADDRTLV_LOCAL_IF, RFC5444_LOCALIF_OTHER_IF);
+                    add_tmp->address->in_tmp_table = NHDP_ADDR_TMP_ANY;
+                }
+            }
+        }
+    }
+
+    mutex_unlock(&mtx_lib_access);
+}
+
+uint8_t lib_is_reg_addr(kernel_pid_t if_pid, nhdp_addr_t *addr)
+{
+    lib_entry_t *lib_elt;
+    nhdp_addr_entry_t *addr_elt;
+
+    LL_FOREACH(lib_entry_head, lib_elt) {
+        LL_FOREACH(lib_elt->if_addr_list_head, addr_elt) {
+            if (addr_elt->address == addr) {
+                if (lib_elt->if_pid == if_pid) {
+                    /* Given address is assigned to the given IF */
+                    return 1;
+                }
+
+                /* Given address is assigned to any other IF */
+                return 2;
+            }
+        }
+    }
+    return 0;
+}
+
+
+/*------------------------------------------------------------------------------------*/
+/*                                Internal functions                                  */
+/*------------------------------------------------------------------------------------*/
+
+/**
+ * Create an entry for a newly registered interface
+ */
+static int create_if_entry(kernel_pid_t if_pid, nhdp_addr_t *addr)
+{
+    lib_entry_t *new_entry;
+
+    new_entry = (lib_entry_t *) malloc(sizeof(lib_entry_t));
+
+    if (!new_entry) {
+        /* Insufficient memory */
+        return -1;
+    }
+
+    new_entry->if_addr_list_head = NULL;
+    new_entry->if_pid = if_pid;
+
+    if (add_address_to_if(new_entry, addr)) {
+        /* Insufficient memory */
+        free(new_entry);
+        return -1;
+    }
+
+    LL_PREPEND(lib_entry_head, new_entry);
+
+    return 0;
+}
+
+/**
+ * Add another address to an interface entry
+ */
+static int add_address_to_if(lib_entry_t *if_entry, nhdp_addr_t *addr)
+{
+    nhdp_addr_entry_t *new_entry = (nhdp_addr_entry_t *) malloc(sizeof(nhdp_addr_entry_t));
+
+    if (!new_entry) {
+        /* Insufficient memory */
+        return -1;
+    }
+
+    /* Increment usage counter of address in central NHDP address storage */
+    addr->usg_count++;
+    new_entry->address = addr;
+    LL_PREPEND(if_entry->if_addr_list_head, new_entry);
+
+    return 0;
+}
diff --git a/sys/net/routing/nhdp/lib_table.h b/sys/net/routing/nhdp/lib_table.h
new file mode 100644
index 0000000000..2425e75aa9
--- /dev/null
+++ b/sys/net/routing/nhdp/lib_table.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     nhdp
+ * @{
+ *
+ * @file
+ * @brief       Local Information Base interface for NHDP
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ */
+
+#ifndef LIB_TABLE_H_
+#define LIB_TABLE_H_
+
+#include "kernel_types.h"
+
+#include "rfc5444/rfc5444_writer.h"
+
+#include "nhdp_address.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Local Interface Set entry (local interface tuple)
+ */
+typedef struct lib_entry_t {
+    kernel_pid_t if_pid;                    /**< PID of the interface's handling thread */
+    nhdp_addr_entry_t *if_addr_list_head;   /**< Pointer to head of this interface's addr list */
+    struct lib_entry_t *next;               /**< Pointer to next list entry */
+} lib_entry_t;
+
+/**
+ * @brief                   Add an interface to the Local Information Base
+ *
+ * This function can also be used to add an additional address to an existing LIB tuple.
+ *
+ * @param[in] if_pid        PID of the interface
+ * @param[in] addr          The (additional) NHDP address to register for the interface
+ *
+ * @return                  0 on success
+ * @return                  -1 on error
+ */
+int lib_add_if_addr(kernel_pid_t if_pid, nhdp_addr_t *addr);
+
+/**
+ * @brief                   Remove a given interface's Local Information Base entry
+ *
+ * @param[in] if_pid        PID of the interface that should be removed
+ */
+void lib_rem_if(kernel_pid_t if_pid);
+
+/**
+ * @brief                   Add addresses to the currently constructed HELLO message
+ *
+ * @note
+ * Must not be called from outside the NHDP writer's message creation process.
+ *
+ * @param[in] if_pid        PID of the interface the message is constructed for
+ * @param[in] wr            The NHDP writer used for message construction
+ */
+void lib_fill_wr_addresses(kernel_pid_t if_pid, struct rfc5444_writer *wr);
+
+/**
+ * @brief                   Check whether a given NHDP address is used as a local address
+ *
+ * @param[in] if_pid        PID of the interface to check for
+ * @param[in] addr          Pointer to the NHDP address that has to be checked
+ *
+ * @return                  1 if the given address is assigned to the given interface
+ * @return                  2 if the given address is assigned to any other local interface
+ * @return                  0 otherwise
+ */
+uint8_t lib_is_reg_addr(kernel_pid_t if_pid, nhdp_addr_t *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIB_TABLE_H_ */
+/** @} */
diff --git a/sys/net/routing/nhdp/nhdp.c b/sys/net/routing/nhdp/nhdp.c
new file mode 100644
index 0000000000..e582dc340a
--- /dev/null
+++ b/sys/net/routing/nhdp/nhdp.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     nhdp
+ * @{
+ *
+ * @file
+ * @brief       Implementation of NHDP's core functionality
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ *
+ * @}
+ */
+
+#include "msg.h"
+#include "netapi.h"
+#include "thread.h"
+#include "utlist.h"
+#include "mutex.h"
+
+#include "rfc5444/rfc5444_writer.h"
+
+#include "lib_table.h"
+#include "iib_table.h"
+#include "nib_table.h"
+#include "nhdp.h"
+#include "nhdp_address.h"
+#include "nhdp_writer.h"
+#include "nhdp_reader.h"
+
+char nhdp_stack[NHDP_STACK_SIZE];
+char nhdp_rcv_stack[NHDP_STACK_SIZE];
+
+/* Internal variables */
+static kernel_pid_t nhdp_pid = KERNEL_PID_UNDEF;
+static kernel_pid_t nhdp_rcv_pid = KERNEL_PID_UNDEF;
+static kernel_pid_t helper_pid = KERNEL_PID_UNDEF;
+static nhdp_if_entry_t *nhdp_if_entry_head = NULL;
+static mutex_t send_rcv_mutex = MUTEX_INIT;
+static sockaddr6_t sa_bcast;
+static int sock_rcv;
+
+/* Internal function prototypes */
+static void *_nhdp_runner(void *arg __attribute__((unused)));
+static void *_nhdp_receiver(void *arg __attribute__((unused)));
+static void write_packet(struct rfc5444_writer *wr __attribute__((unused)),
+                         struct rfc5444_writer_target *iface __attribute__((unused)),
+                         void *buffer, size_t length);
+
+/*---------------------------------------------------------------------------*
+ *                            NHDP Core API                                  *
+ *---------------------------------------------------------------------------*/
+
+void nhdp_init(void)
+{
+    if (nhdp_pid != KERNEL_PID_UNDEF) {
+        /* do not initialize twice */
+        return;
+    }
+
+    /* Initialize reader and writer */
+    nhdp_writer_init();
+    nhdp_reader_init();
+}
+
+kernel_pid_t nhdp_start(void)
+{
+    if (nhdp_pid == KERNEL_PID_UNDEF) {
+        /* Init destination address for NHDP's packets */
+        sa_bcast.sin6_family = AF_INET6;
+        sa_bcast.sin6_port = HTONS(MANET_PORT);
+        ipv6_addr_set_all_nodes_addr(&sa_bcast.sin6_addr);
+
+        /* Configure sending/receiving UDP socket */
+        sock_rcv = socket_base_socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+
+        /* Start the NHDP thread */
+        nhdp_pid = thread_create(nhdp_stack, sizeof(nhdp_stack), PRIORITY_MAIN - 1,
+                                 CREATE_STACKTEST, _nhdp_runner, NULL, "NHDP");
+    }
+
+    return nhdp_pid;
+}
+
+int nhdp_register_if_default(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size,
+                             uint8_t addr_type, uint16_t max_pl_size)
+{
+    return nhdp_register_if(if_pid, addr, addr_size, addr_type, max_pl_size,
+                            NHDP_DEFAULT_HELLO_INT_MS, NHDP_DEFAULT_HOLD_TIME_MS);
+}
+
+int nhdp_register_if(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, uint8_t addr_type,
+                     uint16_t max_pl_size, uint16_t hello_int_ms, uint16_t val_time_ms)
+{
+    nhdp_if_entry_t *if_entry;
+    nhdp_addr_t *nhdp_addr;
+    msg_t signal_msg;
+
+    if (nhdp_rcv_pid != KERNEL_PID_UNDEF) {
+        return -2;
+    }
+
+    if_entry = (nhdp_if_entry_t *) malloc(sizeof(nhdp_if_entry_t));
+
+    if (!if_entry) {
+        /* Insufficient memory */
+        return -1;
+    }
+
+    /* Create an interface writer targer for the nhdp_writer */
+    if_entry->wr_target = (struct rfc5444_writer_target *)
+                          calloc(1, sizeof(struct rfc5444_writer_target));
+
+    if (!if_entry->wr_target) {
+        /* Insufficient memory */
+        free(if_entry);
+        return -1;
+    }
+
+    uint16_t payload_size = max_pl_size > NHDP_MAX_RFC5444_PACKET_SZ
+                            ? NHDP_MAX_RFC5444_PACKET_SZ : max_pl_size;
+    if_entry->wr_target->packet_buffer = (uint8_t *) calloc(payload_size, sizeof(uint8_t));
+
+    if (!if_entry->wr_target->packet_buffer) {
+        /* Insufficient memory */
+        free(if_entry->wr_target);
+        free(if_entry);
+        return -1;
+    }
+
+    if_entry->wr_target->packet_size = payload_size;
+    if_entry->wr_target->sendPacket = write_packet;
+
+    /* Get NHDP address entry for the given address */
+    nhdp_addr = nhdp_addr_db_get_address(addr, addr_size, addr_type);
+
+    if (!nhdp_addr) {
+        /* Insufficient memory */
+        free(if_entry->wr_target->packet_buffer);
+        free(if_entry->wr_target);
+        free(if_entry);
+        return -1;
+    }
+
+    /* Set Interface's PID */
+    if_entry->if_pid = if_pid;
+    /* Set HELLO_INTERVAL and H_HOLD_TIME (validity time) */
+    if_entry->hello_interval.seconds = 0;
+    if_entry->hello_interval.microseconds = MS_IN_USEC * hello_int_ms;
+    if_entry->validity_time.seconds = 0;
+    if_entry->validity_time.microseconds = MS_IN_USEC * val_time_ms;
+    timex_normalize(&if_entry->hello_interval);
+    timex_normalize(&if_entry->validity_time);
+
+    /* Add the interface to the LIB */
+    if (lib_add_if_addr(if_entry->if_pid, nhdp_addr) != 0) {
+        free(if_entry->wr_target->packet_buffer);
+        free(if_entry->wr_target);
+        free(if_entry);
+        nhdp_decrement_addr_usage(nhdp_addr);
+        return -1;
+    }
+
+    /* Create new IIB for the interface */
+    if (iib_register_if(if_pid) != 0) {
+        /* TODO: Cleanup lib entry */
+        free(if_entry->wr_target->packet_buffer);
+        free(if_entry->wr_target);
+        free(if_entry);
+        nhdp_decrement_addr_usage(nhdp_addr);
+        return -1;
+    }
+
+    /* Everything went well */
+    nhdp_decrement_addr_usage(nhdp_addr);
+    nhdp_writer_register_if(if_entry->wr_target);
+    LL_PREPEND(nhdp_if_entry_head, if_entry);
+    helper_pid = if_pid;
+
+    /* Start the receiving thread */
+    nhdp_rcv_pid = thread_create(nhdp_rcv_stack, sizeof(nhdp_rcv_stack), PRIORITY_MAIN - 1,
+                                 CREATE_STACKTEST, _nhdp_receiver, NULL, "nhdp_rcv_thread");
+
+    /* Start sending periodic HELLO */
+    signal_msg.type = MSG_TIMER;
+    signal_msg.content.ptr = (char *) if_entry;
+    /* TODO: msg_send or msg_try_send? */
+    msg_try_send(&signal_msg, nhdp_pid);
+
+    return 0;
+}
+
+int nhdp_register_non_manet_if(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size,
+                               uint8_t addr_type)
+{
+    return nhdp_add_address(if_pid, addr, addr_size, addr_type);
+}
+
+int nhdp_add_address(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, uint8_t addr_type)
+{
+    int result;
+
+    /* Get NHDP address entry for the given address */
+    nhdp_addr_t *nhdp_addr = nhdp_addr_db_get_address(addr, addr_size, addr_type);
+
+    if (!nhdp_addr) {
+        /* Insufficient memory */
+        return -1;
+    }
+
+    result = lib_add_if_addr(if_pid, nhdp_addr);
+    nhdp_decrement_addr_usage(nhdp_addr);
+
+    return result;
+}
+
+/*------------------------------------------------------------------------------------*/
+/*                                Internal functions                                  */
+/*------------------------------------------------------------------------------------*/
+
+/**
+ * Function executed by NHDP thread receiving messages in an endless loop
+ */
+static void *_nhdp_runner(void *arg)
+{
+    nhdp_if_entry_t *if_entry;
+    msg_t msg_rcvd, msg_queue[NHDP_MSG_QUEUE_SIZE];
+
+    (void)arg;
+    msg_init_queue(msg_queue, NHDP_MSG_QUEUE_SIZE);
+
+    while (1) {
+        msg_receive(&msg_rcvd);
+
+        switch (msg_rcvd.type) {
+            case MSG_TIMER:
+                mutex_lock(&send_rcv_mutex);
+                if_entry = (nhdp_if_entry_t *) msg_rcvd.content.ptr;
+
+                nhdp_writer_send_hello(if_entry);
+
+                /* TODO: Add jitter */
+
+                /* Schedule next sending */
+                vtimer_set_msg(&if_entry->if_timer, if_entry->hello_interval,
+                               thread_getpid(), MSG_TIMER, (void *) if_entry);
+                mutex_unlock(&send_rcv_mutex);
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * Receive HELLOs over the configured socket and handle them
+ */
+static void *_nhdp_receiver(void *arg __attribute__((unused)))
+{
+    uint32_t fromlen;
+    char nhdp_rcv_buf[NHDP_MAX_RFC5444_PACKET_SZ];
+    msg_t msg_q[NHDP_MSG_QUEUE_SIZE];
+
+    msg_init_queue(msg_q, NHDP_MSG_QUEUE_SIZE);
+
+    /* Configure socket address for the manet port 269 */
+    sockaddr6_t sa_rcv = {.sin6_family = AF_INET6,
+                          .sin6_port = HTONS(MANET_PORT)
+                         };
+
+    /* Bind UDP socket to socket address */
+    if (socket_base_bind(sock_rcv, &sa_rcv, sizeof(sa_rcv)) == -1) {
+        /* Failed binding the socket */
+        socket_base_close(sock_rcv);
+        return 0;
+    }
+
+    while (1) {
+        int32_t rcv_size = socket_base_recvfrom(sock_rcv, (void *)nhdp_rcv_buf,
+                                                NHDP_MAX_RFC5444_PACKET_SZ, 0, &sa_rcv, &fromlen);
+
+        if (rcv_size > 0) {
+            /* Packet received, let the reader handle it */
+            mutex_lock(&send_rcv_mutex);
+            nhdp_reader_handle_packet(helper_pid, (void *)nhdp_rcv_buf, rcv_size);
+            mutex_unlock(&send_rcv_mutex);
+        }
+    }
+
+    socket_base_close(sock_rcv);
+    return 0;
+}
+
+/**
+ * Send packet for the registered interface
+ * Called by oonf_api to send packet over the configured socket
+ */
+static void write_packet(struct rfc5444_writer *wr __attribute__((unused)),
+                         struct rfc5444_writer_target *iface __attribute__((unused)),
+                         void *buffer, size_t length)
+{
+    socket_base_sendto(sock_rcv, buffer, length, 0, &sa_bcast, sizeof(sa_bcast));
+}
diff --git a/sys/net/routing/nhdp/nhdp.h b/sys/net/routing/nhdp/nhdp.h
new file mode 100644
index 0000000000..2465205319
--- /dev/null
+++ b/sys/net/routing/nhdp/nhdp.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @defgroup    nhdp NHDP
+ * @ingroup     net
+ * @brief       The MANET Neighborhood Discovery Protocol (RFC 6130)
+ * @{
+ *
+ * @file
+ * @brief       Interface for core functionality of NHDP
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ */
+
+#ifndef NHDP_H_
+#define NHDP_H_
+
+#include "timex.h"
+#include "kernel_types.h"
+#include "socket_base/socket.h"
+
+#include "rfc5444/rfc5444_writer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @brief Definition for RIOTs debugging option */
+#define ENABLE_DEBUG (0)
+#include "debug.h"
+
+/**
+ * @name    NHDP protocol macros
+ *
+ * @{
+ */
+#ifndef MANET_PORT
+/** @brief Well-known MANET port from RFC 5498 */
+#define MANET_PORT                  (269)
+#endif
+
+/** @brief Stack size for NHDP thread */
+#if ENABLE_DEBUG
+#define NHDP_STACK_SIZE      (KERNEL_CONF_STACKSIZE_DEFAULT + KERNEL_CONF_STACKSIZE_PRINTF)
+#else
+#define NHDP_STACK_SIZE      (KERNEL_CONF_STACKSIZE_DEFAULT)
+#endif
+
+/** @brief Buffer size in bytes for NHDP writer's msg buffer */
+#define NHDP_WR_MSG_BUF_SIZE        (256)
+/** @brief Buffer size in bytes for NHDP writer's tlv buffer */
+#define NHDP_WR_TLV_BUF_SIZE        (512)
+
+/* TODO: Determine a good value */
+/** @brief Queue size for msg queue of NHDP */
+#define NHDP_MSG_QUEUE_SIZE         (16)
+
+/** @brief Maximum size of handled RFC5444 packets */
+#define NHDP_MAX_RFC5444_PACKET_SZ  (128)
+
+/** @brief Default hello interval in milliseconds */
+#define NHDP_DEFAULT_HELLO_INT_MS   (2000)
+/** @brief Default hold time in milliseconds */
+#define NHDP_DEFAULT_HOLD_TIME_MS   (3 * NHDP_DEFAULT_HELLO_INT_MS)
+
+/**
+ * @brief   Initial pending flag value for new link tuples
+ *
+ *          Do not change (link quality currently not considered)
+ */
+#define NHDP_INITIAL_PENDING        (0)
+
+/** @brief Maximum jitter for nhdp messages in milliseconds */
+#define NHDP_HP_MAXJITTER_MS        (200)
+
+#define NHDP_L_HOLD_TIME_MS         (NHDP_DEFAULT_HOLD_TIME_MS)
+#define NHDP_N_HOLD_TIME_MS         (NHDP_DEFAULT_HOLD_TIME_MS)
+#define NHDP_I_HOLD_TIME_MS         (NHDP_DEFAULT_HOLD_TIME_MS)
+/** @} */
+
+/**
+ * @brief   MANET interface representation
+ */
+typedef struct nhdp_if_entry_t {
+    kernel_pid_t if_pid;                        /**< PID of the interface's handling thread */
+    vtimer_t if_timer;                          /**< Vtimer used for the periodic signaling */
+    timex_t hello_interval;                     /**< Interval time for periodic HELLOs */
+    timex_t validity_time;                      /**< Validity time for propagated information */
+    struct rfc5444_writer_target *wr_target;    /**< Interface specific writer target */
+    struct nhdp_if_entry_t *next;               /**< Pointer to next list entry */
+} nhdp_if_entry_t;
+
+/**
+ * @brief   Additional address types for link layer operation
+ */
+enum nhdp_address_type_t {
+    AF_CC110X = AF_MAX + 3,
+};
+
+/**
+ * @brief                   Initialize NHDP for operation
+ *
+ * Sets up NHDP's reader and writer. Call first before starting NHDP's thread
+ * and registering interfaces.
+ */
+void nhdp_init(void);
+
+/**
+ * @brief                   Start NHDP's operational thread
+ *
+ * @return                  PID of NHDP's operational thread
+ * @return                  KERNEL_PID_UNDEF on error
+ */
+kernel_pid_t nhdp_start(void);
+
+/**
+ * @brief                   Register an interface for NHDP operation with default values
+ *
+ * Registers the interface completely for NHDP operation. Registration includes a new
+ * Local Information Base entry, a new Interface Information Base and starting
+ * the periodical HELLO messaging.
+ *
+ * @note
+ * Default values are a hello interval of 2 seconds and a validity time
+ * of 6 seconds for propagated information.
+ *
+ * @param[in] if_pid        PID of the interface
+ * @param[in] addr          A local address of this interface represented in bytes
+ * @param[in] addr_size     Length in bytes of the local address
+ * @param[in] addr_type     AF type of the local address
+ * @param[in] max_pl_size   Maximum payload size for packets send over this interface
+ *
+ * @return                  0 on success
+ * @return                  -1 on error
+ * @return                  -2 on maximum number of interfaces registered
+ */
+int nhdp_register_if_default(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size,
+                             uint8_t addr_type, uint16_t max_pl_size);
+
+/**
+ * @brief                   Register an interface for NHDP operation
+ *
+ * Registers the interface completely for NHDP operation. Registration includes a new
+ * Local Information Base entry, a new Interface Information Base and starting
+ * the periodical HELLO messaging.
+ *
+ * @param[in] if_pid        PID of the interface
+ * @param[in] addr          A local address of this interface represented in bytes
+ * @param[in] addr_size     Length of the local address (number of bytes)
+ * @param[in] addr_type     AF type of the given address
+ * @param[in] max_pl_size   Maximum payload size for packets send over this interface
+ * @param[in] hello_int_ms  Hello interval in ms for periodic message generation
+ * @param[in] val_time_ms   Validity time in ms for propagated information
+ *
+ * @return                  0 on success
+ * @return                  -1 on error
+ * @return                  -2 on maximum number of interfaces registered
+ */
+int nhdp_register_if(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, uint8_t addr_type,
+                     uint16_t max_pl_size, uint16_t hello_int_ms, uint16_t val_time_ms);
+
+/**
+ * @brief                   Register a non MANET interface in NHDP
+ *
+ * The registered interface does not take part in NHDP operation. Its addresses
+ * will be represented only in other interfaces' messages. No periodical messages
+ * are created for this interface and no message processing is done.
+ *
+ * @param[in] if_pid        PID of the interface
+ * @param[in] addr          A local address of this interface represented in bytes
+ * @param[in] addr_size     Length of the local address (number of bytes)
+ * @param[in] addr_type     AF type of the given address
+ *
+ * @return                  0 on success
+ * @return                  -1 on error
+ */
+int nhdp_register_non_manet_if(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size,
+                               uint8_t addr_type);
+
+/**
+ * @brief                   Register an additional address for an interface in NHDP
+ *
+ * The address is added to the Local Information Base entry of this interface and
+ * will afterwards be propagated as a local address in newly created HELLO messages.
+ *
+ * @param[in] if_pid        PID of the interface
+ * @param[in] addr          Additional local address of this interface represented in bytes
+ * @param[in] addr_size     Length of the local address (number of bytes)
+ * @param[in] addr_type     AF type of the given address
+ *
+ * @return                  0 on success
+ * @return                  -1 on error
+ */
+int nhdp_add_address(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, uint8_t addr_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NHDP_H_ */
+/** @} */
diff --git a/sys/net/routing/nhdp/nhdp_address.c b/sys/net/routing/nhdp/nhdp_address.c
new file mode 100644
index 0000000000..50ab72a697
--- /dev/null
+++ b/sys/net/routing/nhdp/nhdp_address.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     nhdp
+ * @{
+ *
+ * @file
+ * @brief       Centralized address storage implementation for NHDP
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ *
+ * @}
+ */
+
+#include "mutex.h"
+#include "utlist.h"
+
+#include "nhdp.h"
+#include "nhdp_address.h"
+
+/* Internal variables */
+static mutex_t mtx_addr_access = MUTEX_INIT;
+static nhdp_addr_t *nhdp_addr_db_head = NULL;
+
+
+/*---------------------------------------------------------------------------*
+ *                      Centralized Address Storage API                      *
+ *---------------------------------------------------------------------------*/
+
+nhdp_addr_t *nhdp_addr_db_get_address(uint8_t *addr, size_t addr_size, uint8_t addr_type)
+{
+    nhdp_addr_t *addr_elt;
+
+    mutex_lock(&mtx_addr_access);
+
+    LL_FOREACH(nhdp_addr_db_head, addr_elt) {
+        if ((addr_elt->addr_size == addr_size) && (addr_elt->addr_type == addr_type)) {
+            if (memcmp(addr_elt->addr, addr, addr_size) == 0) {
+                /* Found a matching entry */
+                break;
+            }
+        }
+    }
+
+    if (!addr_elt) {
+        /* No matching entry, create a new one */
+        addr_elt = (nhdp_addr_t *) malloc(sizeof(nhdp_addr_t));
+
+        if (!addr_elt) {
+            /* Insufficient memory */
+            return NULL;
+        }
+
+        /* Allocate space for the address */
+        addr_elt->addr = (uint8_t *) malloc(addr_size * sizeof(uint8_t));
+
+        if (!addr_elt->addr) {
+            /* Insufficient memory */
+            free(addr_elt);
+            return NULL;
+        }
+
+        memcpy(addr_elt->addr, addr, addr_size);
+        addr_elt->addr_size = addr_size;
+        addr_elt->addr_type = addr_type;
+        addr_elt->usg_count = 0;
+        addr_elt->in_tmp_table = NHDP_ADDR_TMP_NONE;
+        LL_PREPEND(nhdp_addr_db_head, addr_elt);
+    }
+
+    addr_elt->usg_count++;
+
+    mutex_unlock(&mtx_addr_access);
+
+    return addr_elt;
+}
+
+void nhdp_decrement_addr_usage(nhdp_addr_t *addr)
+{
+    mutex_lock(&mtx_addr_access);
+
+    /* Decrement usage count and delete address if no longer used */
+    if (addr) {
+        addr->usg_count--;
+
+        if (addr->usg_count <= 0) {
+            /* Free address space if address is no longer used */
+            LL_DELETE(nhdp_addr_db_head, addr);
+            free(addr->addr);
+            free(addr);
+        }
+    }
+
+    mutex_unlock(&mtx_addr_access);
+}
+
+void nhdp_free_addr_list(nhdp_addr_entry_t *list_head)
+{
+    nhdp_addr_entry_t *list_elt, *list_tmp;
+
+    LL_FOREACH_SAFE(list_head, list_elt, list_tmp) {
+        nhdp_free_addr_entry(list_elt);
+    }
+}
+
+void nhdp_free_addr_entry(nhdp_addr_entry_t *addr_entry)
+{
+    nhdp_decrement_addr_usage(addr_entry->address);
+    free(addr_entry);
+}
+
+nhdp_addr_entry_t *nhdp_generate_new_addr_list(nhdp_addr_entry_t *orig_list)
+{
+    nhdp_addr_entry_t *new_list_head, *addr_elt;
+
+    new_list_head = NULL;
+    LL_FOREACH(orig_list, addr_elt) {
+        nhdp_addr_entry_t *new_entry = (nhdp_addr_entry_t *) malloc(sizeof(nhdp_addr_entry_t));
+
+        if (!new_entry) {
+            /* Insufficient memory, free all previously allocated memory */
+            nhdp_free_addr_list(new_list_head);
+            return NULL;
+        }
+
+        new_entry->address = addr_elt->address;
+        /* Increment usage counter of address in central NHDP address storage */
+        addr_elt->address->usg_count++;
+        LL_PREPEND(new_list_head, new_entry);
+    }
+
+    return new_list_head;
+}
+
+void nhdp_reset_addresses_tmp_usg(void)
+{
+    nhdp_addr_t *addr_elt;
+
+    LL_FOREACH(nhdp_addr_db_head, addr_elt) {
+        addr_elt->in_tmp_table = NHDP_ADDR_TMP_NONE;
+    }
+}
diff --git a/sys/net/routing/nhdp/nhdp_address.h b/sys/net/routing/nhdp/nhdp_address.h
new file mode 100644
index 0000000000..2d9b6195df
--- /dev/null
+++ b/sys/net/routing/nhdp/nhdp_address.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     nhdp
+ * @{
+ *
+ * @file
+ * @brief       Centralized address storage interface for NHDP
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ */
+
+#ifndef NHDP_ADDRESS_H_
+#define NHDP_ADDRESS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   NHDP address representation
+ */
+typedef struct nhdp_addr_t {
+    uint8_t *addr;                      /**< Pointer to the address data */
+    size_t addr_size;                   /**< Size in bytes of the address */
+    uint8_t addr_type;                  /**< AF type for the address */
+    uint8_t usg_count;                  /**< Usage count in information bases */
+    uint8_t in_tmp_table;               /**< Signals usage in a writers temp table */
+    struct nhdp_addr_t *next;           /**< Pointer to next address (used in central storage) */
+} nhdp_addr_t;
+
+/**
+ * @brief   Container for NHDP address storage in a list
+ */
+typedef struct nhdp_addr_entry_t {
+    struct nhdp_addr_t *address;        /**< Pointer to NHDP address storage entry */
+    struct nhdp_addr_entry_t *next;     /**< Pointer to the next address list element */
+} nhdp_addr_entry_t;
+
+/**
+ * @name    NHDP address temp usage helper macros
+ *
+ * @{
+ */
+#define NHDP_ADDR_TMP_NONE          (0x00)
+#define NHDP_ADDR_TMP_ANY           (0x01)
+#define NHDP_ADDR_TMP_SYM           (0x03)
+
+#define NHDP_ADDR_TMP_IN_SYM(addr)  ((addr->in_tmp_table & 0x02) >> 1)
+#define NHDP_ADDR_TMP_IN_ANY(addr)  ((addr->in_tmp_table & 0x01))
+/** @} */
+
+/**
+ * @brief                   Get or create a NHDP address for the given address
+ *
+ * @param[in] addr          Pointer to the given address
+ * @param[in] addr_size     Length in bytes of the given address
+ * @param[in] addr_type     AF type of the given address
+ *
+ * @return                  Pointer to the NHDP address representation of the given address
+ * @return                  NULL on error
+ */
+nhdp_addr_t *nhdp_addr_db_get_address(uint8_t *addr, size_t addr_size, uint8_t addr_type);
+
+/**
+ * @brief                   Decrement the usage counter of a given NHDP address
+ *
+ * The NHDP address is deleted if the usage counter reaches zero.
+ *
+ * @param[in] addr          Pointer to the NHDP address
+ */
+void nhdp_decrement_addr_usage(nhdp_addr_t *addr);
+
+/**
+ * @brief                   Free the given address list
+ *
+ * This function frees every address list entry of the given address list.
+ *
+ * @param[in] list_head     Pointer to the head of the address list to free
+ */
+void nhdp_free_addr_list(nhdp_addr_entry_t *list_head);
+
+/**
+ * @brief                   Free the given address list entry
+ *
+ * Additionally to freeing the address entry, this function takes care that
+ * the usage counter of the list entry's address is decremented.
+ *
+ * @param[in] addr_entry    Pointer to the address list entry to free
+ */
+void nhdp_free_addr_entry(nhdp_addr_entry_t *addr_entry);
+
+/**
+ * @brief                   Construct an address list containing the addresses of the given list
+ *
+ * @param[in] orig_list     Pointer to the head of the address list to 'clone'
+ *
+ * @return                  Pointer to the head of the newly created address list
+ * @return                  NULL on error
+ */
+nhdp_addr_entry_t *nhdp_generate_new_addr_list(nhdp_addr_entry_t *orig_list);
+
+/**
+ * @brief                   Reset in_tmp_table flag of all NHDP addresses
+ *
+ * @note
+ * Must not be called from outside the NHDP writer's message creation process.
+ */
+void nhdp_reset_addresses_tmp_usg(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NHDP_ADDRESS_H_ */
+/** @} */
diff --git a/sys/net/routing/nhdp/nhdp_reader.c b/sys/net/routing/nhdp/nhdp_reader.c
new file mode 100644
index 0000000000..d5d152cd7a
--- /dev/null
+++ b/sys/net/routing/nhdp/nhdp_reader.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     nhdp
+ * @{
+ *
+ * @file
+ * @brief       Reader implementation for message processing in NHDP
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ *
+ * @}
+ */
+
+#include "mutex.h"
+#include "utlist.h"
+
+#include "rfc5444/rfc5444.h"
+#include "rfc5444/rfc5444_iana.h"
+#include "rfc5444/rfc5444_reader.h"
+
+#include "lib_table.h"
+#include "nib_table.h"
+#include "iib_table.h"
+#include "nhdp.h"
+#include "nhdp_address.h"
+#include "nhdp_reader.h"
+
+/* Internal variables */
+struct rfc5444_reader reader;
+static mutex_t mtx_packet_handler = MUTEX_INIT;
+
+static nhdp_addr_entry_t *send_addr_list_head;
+static nhdp_addr_entry_t *nb_addr_list_head;
+static nhdp_addr_entry_t *th_sym_addr_list_head;
+static nhdp_addr_entry_t *th_rem_addr_list_head;
+static nhdp_addr_entry_t *rem_addr_list_head;
+
+static kernel_pid_t if_pid;
+static uint64_t val_time;
+static uint64_t int_time;
+static uint8_t sym = 0;
+static uint8_t lost = 0;
+
+/* Internal function prototypes */
+static enum rfc5444_result _nhdp_blocktlv_msg_cb(struct rfc5444_reader_tlvblock_context *cont);
+static enum rfc5444_result _nhdp_blocktlv_address_cb(struct rfc5444_reader_tlvblock_context *cont);
+static enum rfc5444_result _nhdp_msg_end_cb(struct rfc5444_reader_tlvblock_context *cont,
+        bool dropped);
+static enum rfc5444_result check_msg_validity(struct rfc5444_reader_tlvblock_context *cont);
+static enum rfc5444_result check_addr_validity(nhdp_addr_t *addr);
+static nhdp_addr_t *get_nhdp_db_addr(uint8_t *addr, uint8_t prefix);
+static void process_temp_tables(void);
+static void cleanup_temp_addr_lists(void);
+
+/* Array containing the processable message TLVs for HELLO messages */
+static struct rfc5444_reader_tlvblock_consumer_entry _nhdp_msg_tlvs[] = {
+    [RFC5444_MSGTLV_INTERVAL_TIME] = { .type = RFC5444_MSGTLV_INTERVAL_TIME, .mandatory = false },
+    [RFC5444_MSGTLV_VALIDITY_TIME] = { .type = RFC5444_MSGTLV_VALIDITY_TIME, .mandatory = true },
+};
+
+/* Array containing the processable address TLVs for HELLO message address blocks */
+static struct rfc5444_reader_tlvblock_consumer_entry _nhdp_addr_tlvs[] = {
+    [RFC5444_ADDRTLV_LOCAL_IF] = { .type = RFC5444_ADDRTLV_LOCAL_IF },
+    [RFC5444_ADDRTLV_LINK_STATUS] = { .type = RFC5444_ADDRTLV_LINK_STATUS },
+    [RFC5444_ADDRTLV_OTHER_NEIGHB] = { .type = RFC5444_ADDRTLV_OTHER_NEIGHB },
+};
+
+/* oonf_api message consumer used for HELLO message consumption */
+static struct rfc5444_reader_tlvblock_consumer _nhdp_msg_consumer = {
+    .msg_id = RFC5444_MSGTYPE_HELLO,
+    .block_callback = _nhdp_blocktlv_msg_cb,
+    .end_callback = _nhdp_msg_end_cb,
+};
+
+/* oonf_api message consumer user for HELLO message address block consumption */
+static struct rfc5444_reader_tlvblock_consumer _nhdp_address_consumer = {
+    .msg_id = RFC5444_MSGTYPE_HELLO,
+    .addrblock_consumer = true,
+    .block_callback = _nhdp_blocktlv_address_cb,
+};
+
+
+/*---------------------------------------------------------------------------*
+ *                             NHDP Reader API                               *
+ *---------------------------------------------------------------------------*/
+
+void nhdp_reader_init(void)
+{
+    /* Reset locally created address lists */
+    send_addr_list_head = NULL;
+    nb_addr_list_head = NULL;
+    th_sym_addr_list_head = NULL;
+    th_rem_addr_list_head = NULL;
+    rem_addr_list_head = NULL;
+
+    /* Initialize reader */
+    rfc5444_reader_init(&reader);
+
+    /* Register HELLO message consumer */
+    rfc5444_reader_add_message_consumer(&reader, &_nhdp_msg_consumer,
+                                        _nhdp_msg_tlvs, ARRAYSIZE(_nhdp_msg_tlvs));
+    rfc5444_reader_add_message_consumer(&reader, &_nhdp_address_consumer,
+                                        _nhdp_addr_tlvs, ARRAYSIZE(_nhdp_addr_tlvs));
+}
+
+int nhdp_reader_handle_packet(kernel_pid_t rcvg_if_pid, void *buffer, size_t length)
+{
+    int result;
+
+    mutex_lock(&mtx_packet_handler);
+
+    /* Store PID of interface this packet was received on */
+    if_pid = rcvg_if_pid;
+    /* Parse packet with reader */
+    result = rfc5444_reader_handle_packet(&reader, buffer, length);
+
+    mutex_unlock(&mtx_packet_handler);
+
+    return result;
+}
+
+void nhdp_reader_cleanup(void)
+{
+    cleanup_temp_addr_lists();
+    rfc5444_reader_cleanup(&reader);
+}
+
+
+/*------------------------------------------------------------------------------------*/
+/*                                Internal functions                                  */
+/*------------------------------------------------------------------------------------*/
+
+/**
+ * Handle one address and its corresponding TLVs
+ * Called by oonf_api for every included address to allow parsing
+ */
+static enum rfc5444_result
+_nhdp_blocktlv_address_cb(struct rfc5444_reader_tlvblock_context *cont)
+{
+    uint8_t tmp_result;
+    /* Create address list entry to add it later to one of the temp address lists */
+    nhdp_addr_entry_t *current_addr = (nhdp_addr_entry_t *) malloc(sizeof(nhdp_addr_entry_t));
+
+    if (!current_addr) {
+        /* Insufficient memory */
+        return RFC5444_DROP_MESSAGE;
+    }
+
+    /* Get NHDP address for the current netaddr */
+    current_addr->address = get_nhdp_db_addr(&cont->addr._addr[0], cont->addr._prefix_len);
+
+    if (!current_addr->address) {
+        /* Insufficient memory */
+        free(current_addr);
+        return RFC5444_DROP_MESSAGE;
+    }
+
+    /* Check validity of address tlvs */
+    if (check_addr_validity(current_addr->address) != RFC5444_OKAY) {
+        nhdp_free_addr_entry(current_addr);
+        return RFC5444_DROP_MESSAGE;
+    }
+
+    /* Handle address and add it to proper temporary list */
+    if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LOCAL_IF].tlv) {
+        switch (*_nhdp_addr_tlvs[RFC5444_ADDRTLV_LOCAL_IF].tlv->single_value) {
+            case RFC5444_LOCALIF_THIS_IF:
+                LL_PREPEND(send_addr_list_head, current_addr);
+                /* Local IF marked addresses have to be added to two temp lists */
+                nhdp_addr_entry_t *sec_container =
+                    (nhdp_addr_entry_t *) malloc(sizeof(nhdp_addr_entry_t));
+
+                if (!sec_container) {
+                    return RFC5444_DROP_MESSAGE;
+                }
+
+                /* Increment usage counter of address in central NHDP address storage */
+                current_addr->address->usg_count++;
+                sec_container->address = current_addr->address;
+                LL_PREPEND(nb_addr_list_head, sec_container);
+                break;
+
+            case RFC5444_LOCALIF_OTHER_IF:
+                LL_PREPEND(nb_addr_list_head, current_addr);
+                break;
+
+            default:
+                /* Wrong value, drop message */
+                nhdp_free_addr_entry(current_addr);
+                return RFC5444_DROP_MESSAGE;
+        }
+    }
+    else if ((tmp_result = lib_is_reg_addr(if_pid, current_addr->address))) {
+        /* The address is one of our local addresses (do not add it for processing) */
+        if ((!sym) && (tmp_result == 1) && _nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv) {
+            /* If address is a local address of the receiving interface, check */
+            /* whether we can derive a status for this link (symmetry or lost) */
+            switch (*_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv->single_value) {
+                case RFC5444_LINKSTATUS_SYMMETRIC:
+                    /* Fall - through */
+
+                case RFC5444_LINKSTATUS_HEARD:
+                    sym = 1;
+                    break;
+
+                case RFC5444_LINKSTATUS_LOST:
+                    lost = 1;
+                    break;
+
+                default:
+                    /* Wrong value, drop message */
+                    nhdp_free_addr_entry(current_addr);
+                    return RFC5444_DROP_MESSAGE;
+            }
+        }
+
+        /* Address is one of our own addresses, ignore it */
+        nhdp_free_addr_entry(current_addr);
+    }
+    else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv) {
+        switch (*_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv->single_value) {
+            case RFC5444_LINKSTATUS_SYMMETRIC:
+                LL_PREPEND(th_sym_addr_list_head, current_addr);
+                break;
+
+            case RFC5444_LINKSTATUS_HEARD:
+                /* Fall-through */
+
+            case RFC5444_LINKSTATUS_LOST:
+                if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv
+                    && *_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv->single_value
+                    == RFC5444_OTHERNEIGHB_SYMMETRIC) {
+                    /* Symmetric has higher priority */
+                    LL_PREPEND(th_sym_addr_list_head, current_addr);
+                }
+                else {
+                    LL_PREPEND(th_rem_addr_list_head, current_addr);
+                }
+
+                break;
+
+            default:
+                /* Wrong value, drop message */
+                nhdp_free_addr_entry(current_addr);
+                return RFC5444_DROP_MESSAGE;
+        }
+    }
+    else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv) {
+        switch (*_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv->single_value) {
+            case RFC5444_OTHERNEIGHB_SYMMETRIC:
+                LL_PREPEND(th_sym_addr_list_head, current_addr);
+                break;
+
+            case RFC5444_OTHERNEIGHB_LOST:
+                LL_PREPEND(th_rem_addr_list_head, current_addr);
+                break;
+
+            default:
+                /* Wrong value, drop message */
+                nhdp_free_addr_entry(current_addr);
+                return RFC5444_DROP_MESSAGE;
+        }
+    }
+    else {
+        /* Addresses without expected TLV are ignored */
+        nhdp_free_addr_entry(current_addr);
+        return RFC5444_DROP_ADDRESS;
+    }
+
+    return RFC5444_OKAY;
+}
+
+/**
+ * Handle message TLVs of received HELLO
+ * Called by oonf_api to allow message TLV parsing
+ */
+static enum rfc5444_result
+_nhdp_blocktlv_msg_cb(struct rfc5444_reader_tlvblock_context *cont)
+{
+    /* Check whether specified message TLVs are correctly included */
+    if (check_msg_validity(cont) != RFC5444_OKAY) {
+        return RFC5444_DROP_MESSAGE;
+    }
+
+    /* Validity time must be included as message tlv */
+    val_time = rfc5444_timetlv_decode(
+                   *_nhdp_msg_tlvs[RFC5444_MSGTLV_VALIDITY_TIME].tlv->single_value);
+
+    /* Interval time is not mandatory as message tlv */
+    if (_nhdp_msg_tlvs[RFC5444_MSGTLV_INTERVAL_TIME].tlv) {
+        int_time = rfc5444_timetlv_decode(
+                       *_nhdp_msg_tlvs[RFC5444_MSGTLV_INTERVAL_TIME].tlv->single_value);
+    }
+
+    return RFC5444_OKAY;
+}
+
+/**
+ * Process received addresses and clean up temporary stuff
+ * Called by oonf_api after message was parsed
+ */
+static enum rfc5444_result
+_nhdp_msg_end_cb(struct rfc5444_reader_tlvblock_context *cont __attribute__((unused)),
+                 bool dropped)
+{
+    if (!dropped) {
+        /* Only process the received addresses if message was valid */
+        process_temp_tables();
+    }
+
+    /* Clean all temporary stuff */
+    val_time = 0ULL;
+    int_time = 0ULL;
+    sym = 0;
+    lost = 0;
+    cleanup_temp_addr_lists();
+
+    if (dropped) {
+        return RFC5444_DROP_MESSAGE;
+    }
+
+    return RFC5444_OKAY;
+}
+
+/**
+ * Check validity of HELLO message header and message TLVs
+ */
+static enum rfc5444_result check_msg_validity(struct rfc5444_reader_tlvblock_context *cont)
+{
+    if (cont->has_hoplimit && cont->hoplimit != 1) {
+        /* Hop Limit other than 1 */
+        return RFC5444_DROP_MESSAGE;
+    }
+
+    if (cont->has_hopcount && cont->hopcount != 0) {
+        /* Hop Count other than zero */
+        return RFC5444_DROP_MESSAGE;
+    }
+
+    if (!(_nhdp_msg_tlvs[RFC5444_MSGTLV_VALIDITY_TIME].tlv)) {
+        /* No validity time tlv */
+        return RFC5444_DROP_MESSAGE;
+    }
+    else if (_nhdp_msg_tlvs[RFC5444_MSGTLV_VALIDITY_TIME].tlv->next_entry) {
+        /* Multiple validity time tlvs */
+        return RFC5444_DROP_MESSAGE;
+    }
+
+    if (_nhdp_msg_tlvs[RFC5444_MSGTLV_INTERVAL_TIME].tlv
+        && _nhdp_msg_tlvs[RFC5444_MSGTLV_INTERVAL_TIME].tlv->next_entry) {
+        /* Multiple interval time tlvs */
+        return RFC5444_DROP_MESSAGE;
+    }
+
+    return RFC5444_OKAY;
+}
+
+/**
+ * Check validity of address block TLVs
+ */
+static enum rfc5444_result check_addr_validity(nhdp_addr_t *addr)
+{
+    if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LOCAL_IF].tlv) {
+        if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv
+            || _nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv) {
+            /* Conflicting tlv types for the address */
+            return RFC5444_DROP_MESSAGE;
+        }
+        else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LOCAL_IF].tlv->next_entry) {
+            /* Multiple tlvs of the same type are not allowed */
+            return RFC5444_DROP_MESSAGE;
+        }
+        else if (lib_is_reg_addr(if_pid, addr)) {
+            /* Address of one of neighbor's IFs equals one of ours */
+            return RFC5444_DROP_MESSAGE;
+        }
+    }
+    else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv
+             && _nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv->next_entry) {
+        /* Multiple tlvs of the same type are not allowed */
+        return RFC5444_DROP_MESSAGE;
+    }
+    else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv
+             && _nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv->next_entry) {
+        /* Multiple tlvs of the same type are not allowed */
+        return RFC5444_DROP_MESSAGE;
+    }
+
+    return RFC5444_OKAY;
+}
+
+/**
+ * Get a new or existing NHDP address entry from the centralized address storage
+ * for the given address data
+ */
+static nhdp_addr_t *get_nhdp_db_addr(uint8_t *addr, uint8_t prefix)
+{
+    switch (prefix) {
+        case 8:
+            return nhdp_addr_db_get_address(addr, 1, AF_CC110X);
+
+        case 32:
+            return nhdp_addr_db_get_address(addr, 4, AF_INET);
+
+        default:
+            if (prefix < 32) {
+                return nhdp_addr_db_get_address(addr, 4, AF_INET);
+            }
+            else {
+                return nhdp_addr_db_get_address(addr, 16, AF_INET6);
+            }
+    }
+}
+
+/**
+ * Process address lists from the HELLO msg in the information bases
+ */
+static void process_temp_tables(void)
+{
+    nib_entry_t *nib_elt;
+    timex_t now;
+
+    vtimer_now(&now);
+    iib_update_lt_status(&now);
+
+    nib_elt = nib_process_hello(nb_addr_list_head, &rem_addr_list_head);
+
+    if (nib_elt) {
+        iib_process_hello(if_pid, nib_elt, send_addr_list_head, th_sym_addr_list_head,
+                          th_rem_addr_list_head, rem_addr_list_head, val_time, sym, lost);
+    }
+}
+
+/**
+ * Free all allocated space for the temporary address lists
+ */
+static void cleanup_temp_addr_lists(void)
+{
+    nhdp_free_addr_list(send_addr_list_head);
+    nhdp_free_addr_list(nb_addr_list_head);
+    nhdp_free_addr_list(th_sym_addr_list_head);
+    nhdp_free_addr_list(th_rem_addr_list_head);
+    nhdp_free_addr_list(rem_addr_list_head);
+    send_addr_list_head = NULL;
+    nb_addr_list_head = NULL;
+    th_sym_addr_list_head = NULL;
+    th_rem_addr_list_head = NULL;
+    rem_addr_list_head = NULL;
+}
diff --git a/sys/net/routing/nhdp/nhdp_reader.h b/sys/net/routing/nhdp/nhdp_reader.h
new file mode 100644
index 0000000000..15f4af37c2
--- /dev/null
+++ b/sys/net/routing/nhdp/nhdp_reader.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     nhdp
+ * @{
+ *
+ * @file
+ * @brief       Reader interface for message processing in NHDP
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ */
+
+#ifndef NHDP_READER_H_
+#define NHDP_READER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief                   Set up the NHDP reader
+ *
+ * Initializes the reader and registers HELLO messages as a message
+ * type consumed by this reader.
+ */
+void nhdp_reader_init(void);
+
+/**
+ * @brief                   Clean up the NHDP reader
+ */
+void nhdp_reader_cleanup(void);
+
+/**
+ * @brief                   Handle a packet received from a registered interface
+ *
+ * @note
+ * HELLO message processing is triggered from this function.
+ *
+ * @param[in] rcvg_if_pid   PID of the interface the packet was received on
+ * @param[in] buffer        Pointer to start of packet data
+ * @param[in] length        Length in bytes of the packet data
+ *
+ * @return                  rfc5444_result of the packet handling process
+ */
+int nhdp_reader_handle_packet(kernel_pid_t rcvg_if_pid, void *buffer, size_t length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NHDP_READER_H_ */
+/** @} */
diff --git a/sys/net/routing/nhdp/nhdp_writer.c b/sys/net/routing/nhdp/nhdp_writer.c
new file mode 100644
index 0000000000..f4c12a42b3
--- /dev/null
+++ b/sys/net/routing/nhdp/nhdp_writer.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     nhdp
+ * @{
+ *
+ * @file
+ * @brief       Writer implementation for message generation in NHDP
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <string.h>
+
+#include "timex.h"
+#include "mutex.h"
+
+#include "rfc5444/rfc5444.h"
+#include "rfc5444/rfc5444_iana.h"
+#include "rfc5444/rfc5444_writer.h"
+
+#include "nhdp.h"
+#include "nhdp_address.h"
+#include "nhdp_writer.h"
+#include "lib_table.h"
+#include "nib_table.h"
+#include "iib_table.h"
+
+/* Internal variables */
+static mutex_t mtx_packet_write = MUTEX_INIT;
+static struct rfc5444_writer nhdp_writer;
+static nhdp_if_entry_t *nhdp_wr_curr_if_entry;
+static uint8_t msg_buffer[NHDP_WR_MSG_BUF_SIZE];
+static uint8_t msg_addrtlvs[NHDP_WR_TLV_BUF_SIZE];
+
+/* Internal function prototypes */
+static void _nhdp_add_hello_msg_header_cb(struct rfc5444_writer *wr,
+        struct rfc5444_writer_message *msg);
+static void _nhdp_add_message_tlvs_cb(struct rfc5444_writer *wr);
+static void _nhdp_add_addresses_cb(struct rfc5444_writer *wr);
+static void netaddr_from_nhdp_address(struct netaddr *target, nhdp_addr_t *n_addr);
+
+/* Array containing the known Address TLVs */
+static struct rfc5444_writer_tlvtype _nhdp_addrtlvs[] = {
+    [RFC5444_ADDRTLV_LOCAL_IF] = { .type = RFC5444_ADDRTLV_LOCAL_IF },
+    [RFC5444_ADDRTLV_LINK_STATUS] = { .type = RFC5444_ADDRTLV_LINK_STATUS },
+    [RFC5444_ADDRTLV_OTHER_NEIGHB] = { .type = RFC5444_ADDRTLV_OTHER_NEIGHB },
+};
+
+/* Writer content provider for HELLO messages */
+static struct rfc5444_writer_content_provider _nhdp_message_content_provider = {
+    .msg_type = RFC5444_MSGTYPE_HELLO,
+    .addMessageTLVs = _nhdp_add_message_tlvs_cb,
+    .addAddresses = _nhdp_add_addresses_cb,
+};
+
+
+/*---------------------------------------------------------------------------*
+ *                            NHDP Writer API                                *
+ *---------------------------------------------------------------------------*/
+
+void nhdp_writer_init(void)
+{
+    struct rfc5444_writer_message *_hello_msg;
+
+    mutex_lock(&mtx_packet_write);
+
+    /* Reset current interface */
+    nhdp_wr_curr_if_entry = NULL;
+
+    /* Configure NHDP writer */
+    nhdp_writer.msg_buffer = msg_buffer;
+    nhdp_writer.msg_size = sizeof(msg_buffer);
+    nhdp_writer.addrtlv_buffer = msg_addrtlvs;
+    nhdp_writer.addrtlv_size  = sizeof(msg_addrtlvs);
+
+    /* Initialize writer */
+    rfc5444_writer_init(&nhdp_writer);
+
+    /* Register HELLO msg with 16 byte addresses and content provider */
+    rfc5444_writer_register_msgcontentprovider(&nhdp_writer,
+            &_nhdp_message_content_provider, _nhdp_addrtlvs, ARRAYSIZE(_nhdp_addrtlvs));
+    _hello_msg = rfc5444_writer_register_message(&nhdp_writer, RFC5444_MSGTYPE_HELLO, false, 16);
+    _hello_msg->addMessageHeader = _nhdp_add_hello_msg_header_cb;
+
+    mutex_unlock(&mtx_packet_write);
+}
+
+void nhdp_writer_cleanup(void)
+{
+    mutex_lock(&mtx_packet_write);
+
+    nhdp_wr_curr_if_entry = NULL;
+    rfc5444_writer_cleanup(&nhdp_writer);
+
+    mutex_unlock(&mtx_packet_write);
+}
+
+void nhdp_writer_register_if(struct rfc5444_writer_target *new_if)
+{
+    mutex_lock(&mtx_packet_write);
+
+    /* Register target interface in writer */
+    rfc5444_writer_register_target(&nhdp_writer, new_if);
+
+    mutex_unlock(&mtx_packet_write);
+}
+
+void nhdp_writer_send_hello(nhdp_if_entry_t *if_entry)
+{
+    mutex_lock(&mtx_packet_write);
+
+    /* Register interface as current sending interface */
+    nhdp_wr_curr_if_entry = if_entry;
+
+    /* Create HELLO message and send it using the given interface */
+    rfc5444_writer_create_message(&nhdp_writer, RFC5444_MSGTYPE_HELLO,
+                                  rfc5444_writer_singletarget_selector, if_entry->wr_target);
+    rfc5444_writer_flush(&nhdp_writer, if_entry->wr_target, false);
+
+    mutex_unlock(&mtx_packet_write);
+}
+
+void nhdp_writer_add_addr(struct rfc5444_writer *wr, nhdp_addr_t *addr,
+                          enum rfc5444_addrtlv_iana type, uint8_t value)
+{
+    struct rfc5444_writer_address *wr_addr;
+    struct netaddr n_addr;
+
+    netaddr_from_nhdp_address(&n_addr, addr);
+
+    switch (type) {
+        case RFC5444_ADDRTLV_LOCAL_IF:
+            /* Address is mandatory for every sub-msg (if message is splitted) */
+            wr_addr = rfc5444_writer_add_address(wr, _nhdp_message_content_provider.creator,
+                                                 &n_addr, true);
+            break;
+
+        case RFC5444_ADDRTLV_LINK_STATUS:
+            /* Fall through */
+
+        case RFC5444_ADDRTLV_OTHER_NEIGHB:
+            /* Address only has to be included in one sub-msg (if message is splitted) */
+            wr_addr = rfc5444_writer_add_address(wr, _nhdp_message_content_provider.creator,
+                                                 &n_addr, false);
+            break;
+
+        default:
+            /* Unknown type, extend switch if other types are allowed */
+            return;
+    }
+
+    rfc5444_writer_add_addrtlv(wr, wr_addr, &_nhdp_addrtlvs[type],
+                               &value, sizeof(uint8_t), false);
+}
+
+
+/*------------------------------------------------------------------------------------*/
+/*                                Internal functions                                  */
+/*------------------------------------------------------------------------------------*/
+
+/**
+ * Set the header for the currently constructed HELLO message
+ * Called by oonf_api during message creation
+ */
+static void
+_nhdp_add_hello_msg_header_cb(struct rfc5444_writer *wr, struct rfc5444_writer_message *msg)
+{
+    /* No originator, no hopcount, no hoplimit, no sequence number */
+    rfc5444_writer_set_msg_header(wr, msg, false, false, false, false);
+}
+
+/**
+ * Add validity time and interval time message TLVs to current message
+ * Called by oonf_api during message creation
+ */
+static void _nhdp_add_message_tlvs_cb(struct rfc5444_writer *wr)
+{
+    uint8_t validity_time, interval_time;
+    /* Convert validity time and interval time to milliseconds */
+    uint64_t val_tmp = (uint64_t) nhdp_wr_curr_if_entry->validity_time.seconds * SEC_IN_MS
+                       + (nhdp_wr_curr_if_entry->validity_time.microseconds / 1000ULL);
+    uint64_t int_tmp = (uint64_t) nhdp_wr_curr_if_entry->hello_interval.seconds * SEC_IN_MS
+                       + (nhdp_wr_curr_if_entry->hello_interval.microseconds / 1000ULL);
+
+    /* Add validity time (mandatory) and interval time to msg */
+    validity_time = rfc5444_timetlv_encode(val_tmp);
+    interval_time = rfc5444_timetlv_encode(int_tmp);
+    rfc5444_writer_add_messagetlv(wr, RFC5444_MSGTLV_VALIDITY_TIME, 0, &validity_time,
+                                  sizeof(validity_time));
+    rfc5444_writer_add_messagetlv(wr, RFC5444_MSGTLV_INTERVAL_TIME, 0, &interval_time,
+                                  sizeof(interval_time));
+}
+
+/**
+ * Add addresses and corresponding TLVs to current message
+ * Called by oonf_api during message creation
+ */
+static void _nhdp_add_addresses_cb(struct rfc5444_writer *wr)
+{
+    lib_fill_wr_addresses(nhdp_wr_curr_if_entry->if_pid, wr);
+    iib_fill_wr_addresses(nhdp_wr_curr_if_entry->if_pid, wr);
+    nib_fill_wr_addresses(wr);
+    nhdp_reset_addresses_tmp_usg();
+}
+
+/**
+ * Construct a netaddr from a given NHDP address
+ */
+static void netaddr_from_nhdp_address(struct netaddr *target, nhdp_addr_t *n_addr)
+{
+    memset(target->_addr, 0, NETADDR_MAX_LENGTH);
+    memcpy(target->_addr, n_addr->addr, n_addr->addr_size);
+
+    switch (n_addr->addr_type) {
+        case AF_CC110X:
+            target->_prefix_len = 8u;
+            target->_type = AF_CC110X;
+            break;
+
+        case AF_INET:
+            target->_prefix_len = 32u;
+            target->_type = AF_INET;
+            break;
+
+        case AF_INET6:
+            /* Fall-through */
+
+        default:
+            target->_prefix_len = 128u;
+            target->_type = AF_INET6;
+            break;
+    }
+}
diff --git a/sys/net/routing/nhdp/nhdp_writer.h b/sys/net/routing/nhdp/nhdp_writer.h
new file mode 100644
index 0000000000..2998e0f82c
--- /dev/null
+++ b/sys/net/routing/nhdp/nhdp_writer.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     nhdp
+ * @{
+ *
+ * @file
+ * @brief       Writer interface for message generation in NHDP
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ */
+
+#ifndef NHDP_WRITER_H_
+#define NHDP_WRITER_H_
+
+#include "rfc5444/rfc5444_iana.h"
+#include "rfc5444/rfc5444_writer.h"
+
+#include "nhdp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief                   Set up the NHDP writer
+ *
+ * Sets the writer's buffers, initializes the writer and registers HELLO messages
+ * as new message type.
+ */
+void nhdp_writer_init(void);
+
+/**
+ * @brief                   Clean up the NHDP writer
+ */
+void nhdp_writer_cleanup(void);
+
+/**
+ * @brief                   Register a new target interface in the writer
+ *
+ * @param[in] new_if        Pointer to the writer target of the NHDP interface entry
+ */
+void nhdp_writer_register_if(struct rfc5444_writer_target *new_if);
+
+/**
+ * @brief                   Construct and send a HELLO message using the given interface
+ *
+ * @param[in] if_entry      Pointer to NHDP interface entry the message must be created for
+ */
+void nhdp_writer_send_hello(nhdp_if_entry_t *if_entry);
+
+/**
+ * @brief                   Add a NHDP address to the currently constructed message
+ *
+ * @note
+ * Must not be called from outside the NHDP writer's message creation process.
+ *
+ * @param[in] wr            Pointer to the current NHDP writer used for message creation
+ * @param[in] addr          Pointer to a NHDP address to add to the HELLO message
+ * @param[in] type          TLV type for the address
+ * @param[in] value         TLV value for the address
+ */
+void nhdp_writer_add_addr(struct rfc5444_writer *wr, nhdp_addr_t *addr,
+                          enum rfc5444_addrtlv_iana type, uint8_t value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NHDP_WRITER_H_ */
+/** @} */
diff --git a/sys/net/routing/nhdp/nib_table.c b/sys/net/routing/nhdp/nib_table.c
new file mode 100644
index 0000000000..d72a16016e
--- /dev/null
+++ b/sys/net/routing/nhdp/nib_table.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     nhdp
+ * @{
+ *
+ * @file
+ * @brief       Neighbor Information Base implementation for NHDP
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ *
+ * @}
+ */
+
+#include "timex.h"
+#include "mutex.h"
+#include "vtimer.h"
+#include "utlist.h"
+
+#include "rfc5444/rfc5444_iana.h"
+#include "rfc5444/rfc5444_writer.h"
+
+#include "nib_table.h"
+#include "iib_table.h"
+#include "nhdp_address.h"
+#include "nhdp_writer.h"
+
+/* Internal variables */
+static mutex_t mtx_nib_access = MUTEX_INIT;
+static nib_entry_t *nib_entry_head = NULL;
+static nib_lost_address_entry_t *nib_lost_address_entry_head = NULL;
+
+/* Internal function prototypes */
+static nib_entry_t *add_nib_entry(nhdp_addr_entry_t *nb_list);
+static void rem_nib_entry(nib_entry_t *nib_entry, nhdp_addr_entry_t *nb_list,
+                          nhdp_addr_entry_t **out_list, timex_t *now);
+static void clear_nb_addresses(nib_entry_t *nib_entry, nhdp_addr_entry_t *nb_list,
+                               nhdp_addr_entry_t **out_list, timex_t *now);
+static int add_lost_neighbor_address(nhdp_addr_t *lost_addr, timex_t *now);
+static void rem_ln_entry(nib_lost_address_entry_t *ln_entry);
+
+
+/*---------------------------------------------------------------------------*
+ *                       Neighbor Information Base API                       *
+ *---------------------------------------------------------------------------*/
+
+nib_entry_t *nib_process_hello(nhdp_addr_entry_t *nb_list, nhdp_addr_entry_t **out_list)
+{
+    nib_entry_t *nb_match = NULL;
+    timex_t now;
+    uint8_t matches = 0;
+
+    mutex_lock(&mtx_nib_access);
+
+    if (nb_list) {
+        nib_entry_t *nib_elt, *nib_tmp;
+
+        vtimer_now(&now);
+
+        LL_FOREACH_SAFE(nib_entry_head, nib_elt, nib_tmp) {
+            nhdp_addr_entry_t *list_elt;
+            LL_FOREACH(nib_elt->address_list_head, list_elt) {
+                nhdp_addr_entry_t *list_elt2;
+                LL_FOREACH(nb_list, list_elt2) {
+                    if (list_elt->address == list_elt2->address) {
+                        /* Addresses are equal (same NHDP address db entry) */
+                        matches++;
+
+                        if (matches > 1) {
+                            /* Multiple matching nb tuples, delete the previous one */
+                            iib_propagate_nb_entry_change(nb_match, nib_elt);
+                            rem_nib_entry(nb_match, nb_list, out_list, &now);
+                        }
+
+                        nb_match = nib_elt;
+                        break;
+                    }
+                }
+
+                if (nb_match == nib_elt) {
+                    /* This nb tuple is already detected as matching */
+                    break;
+                }
+            }
+        }
+
+        /* Add or update nb tuple */
+        if (matches > 0) {
+            /* We found matching nb tuples, reuse the last one */
+            clear_nb_addresses(nb_match, nb_list, out_list, &now);
+
+            if (matches > 1) {
+                nb_match->symmetric = 0;
+            }
+
+            nb_match->address_list_head = nhdp_generate_new_addr_list(nb_list);
+
+            if (!nb_match->address_list_head) {
+                /* Insufficient memory */
+                LL_DELETE(nib_entry_head, nb_match);
+                free(nb_match);
+                nb_match = NULL;
+            }
+        }
+        else {
+            nb_match = add_nib_entry(nb_list);
+        }
+    }
+
+    mutex_unlock(&mtx_nib_access);
+    return nb_match;
+}
+
+void nib_fill_wr_addresses(struct rfc5444_writer *wr)
+{
+    nib_entry_t *nib_elt;
+    nhdp_addr_entry_t *addr_elt;
+    nib_lost_address_entry_t *lost_elt, *lost_tmp;
+    timex_t now;
+
+    mutex_lock(&mtx_nib_access);
+
+    vtimer_now(&now);
+
+    /* Add addresses of symmetric neighbors to HELLO msg */
+    LL_FOREACH(nib_entry_head, nib_elt) {
+        if (nib_elt->symmetric) {
+            LL_FOREACH(nib_elt->address_list_head, addr_elt) {
+                /* Check whether address is not already included with link status symmetric */
+                if (!NHDP_ADDR_TMP_IN_SYM(addr_elt->address)) {
+                    nhdp_writer_add_addr(wr, addr_elt->address, RFC5444_ADDRTLV_OTHER_NEIGHB,
+                                         RFC5444_OTHERNEIGHB_SYMMETRIC);
+                    addr_elt->address->in_tmp_table = NHDP_ADDR_TMP_SYM;
+                }
+            }
+        }
+    }
+
+    /* Add lost addresses of neighbors to HELLO msg */
+    LL_FOREACH_SAFE(nib_lost_address_entry_head, lost_elt, lost_tmp) {
+        if (timex_cmp(lost_elt->expiration_time, now) != 1) {
+            /* Entry expired, remove it */
+            rem_ln_entry(lost_elt);
+        }
+        else {
+            /* Check if address is not already present in one of the temporary lists */
+            if (!NHDP_ADDR_TMP_IN_ANY(lost_elt->address)) {
+                /* Address is not present in one of the lists, add it */
+                nhdp_writer_add_addr(wr, lost_elt->address, RFC5444_ADDRTLV_OTHER_NEIGHB,
+                                     RFC5444_OTHERNEIGHB_LOST);
+            }
+        }
+    }
+
+    mutex_unlock(&mtx_nib_access);
+}
+
+void nib_rem_nb_entry(nib_entry_t *nib_entry)
+{
+    nhdp_free_addr_list(nib_entry->address_list_head);
+    LL_DELETE(nib_entry_head, nib_entry);
+    free(nib_entry);
+}
+
+void nib_set_nb_entry_sym(nib_entry_t *nib_entry)
+{
+    nib_lost_address_entry_t *ln_elt, *ln_tmp;
+    nhdp_addr_entry_t *nb_elt;
+
+    nib_entry->symmetric = 1;
+    LL_FOREACH(nib_entry->address_list_head, nb_elt) {
+        LL_FOREACH_SAFE(nib_lost_address_entry_head, ln_elt, ln_tmp) {
+            /* Remove all Lost Neighbor Tuples matching an address of the newly sym nb */
+            if (ln_elt->address == nb_elt->address) {
+                rem_ln_entry(ln_elt);
+                break;
+            }
+        }
+    }
+}
+
+void nib_reset_nb_entry_sym(nib_entry_t *nib_entry, timex_t *now)
+{
+    nhdp_addr_entry_t *nb_elt;
+
+    nib_entry->symmetric = 0;
+    LL_FOREACH(nib_entry->address_list_head, nb_elt) {
+        /* Add a Lost Neighbor Tuple for each address of the neighbor */
+        if (add_lost_neighbor_address(nb_elt->address, now) == -1) {
+            /* Insufficient memory */
+            return;
+        }
+    }
+}
+
+
+/*------------------------------------------------------------------------------------*/
+/*                                Internal functions                                  */
+/*------------------------------------------------------------------------------------*/
+
+/**
+ * Add a Neighbor Tuple with the given address list
+ */
+static nib_entry_t *add_nib_entry(nhdp_addr_entry_t *nb_list)
+{
+    nib_entry_t *new_elem;
+
+    new_elem = malloc(sizeof(nib_entry_t));
+
+    if (!new_elem) {
+        /* Insufficient memory */
+        return NULL;
+    }
+
+    /* Copy address list to new neighbor tuple */
+    new_elem->address_list_head = nhdp_generate_new_addr_list(nb_list);
+
+    if (!new_elem->address_list_head) {
+        /* Insufficient memory */
+        free(new_elem);
+        return NULL;
+    }
+
+    new_elem->symmetric = 0;
+    LL_PREPEND(nib_entry_head, new_elem);
+
+    return new_elem;
+}
+
+/**
+ * Remove a given Neighbor Tuple
+ */
+static void rem_nib_entry(nib_entry_t *nib_entry, nhdp_addr_entry_t *nb_list,
+                          nhdp_addr_entry_t **out_list, timex_t *now)
+{
+    clear_nb_addresses(nib_entry, nb_list, out_list, now);
+    LL_DELETE(nib_entry_head, nib_entry);
+    free(nib_entry);
+}
+
+/**
+ * Clear address list of a Neighbor Tuple and add Lost Neighbor Tuple for addresses
+ * no longer used by this neighbor
+ */
+static void clear_nb_addresses(nib_entry_t *nib_entry, nhdp_addr_entry_t *nb_list,
+                               nhdp_addr_entry_t **out_list, timex_t *now)
+{
+    nhdp_addr_entry_t *elt, *nib_elt, *nib_tmp;
+    uint8_t found;
+
+    LL_FOREACH_SAFE(nib_entry->address_list_head, nib_elt, nib_tmp) {
+        found = 0;
+        LL_FOREACH(nb_list, elt) {
+            /* Check whether address is still present in the new neighbor address list */
+            if (nib_elt->address == elt->address) {
+                /* Simply free the address entry */
+                nhdp_free_addr_entry(nib_elt);
+                found = 1;
+                break;
+            }
+        }
+
+        if (!found) {
+            /* Address is not in the newly received address list of the neighbor */
+            /* Add it to the Removed Address List (out_list) */
+            LL_PREPEND(*out_list, nib_elt);
+
+            if (nib_entry->symmetric) {
+                /* Additionally create a Lost Neighbor Tuple for symmetric neighbors */
+                add_lost_neighbor_address(nib_elt->address, now);
+            }
+        }
+    }
+    nib_entry->address_list_head = NULL;
+}
+
+/**
+ * Add or update a Lost Neighbor Tuple
+ */
+static int add_lost_neighbor_address(nhdp_addr_t *lost_addr, timex_t *now)
+{
+    nib_lost_address_entry_t *elt;
+    timex_t n_hold = timex_from_uint64(((uint64_t)NHDP_N_HOLD_TIME_MS) * MS_IN_USEC);
+
+    LL_FOREACH(nib_lost_address_entry_head, elt) {
+        if (elt->address == lost_addr) {
+            /* Existing entry for this address, no need to add a new one */
+            if (timex_cmp(elt->expiration_time, *now) == -1) {
+                /* Entry expired, so just update expiration time */
+                elt->expiration_time = timex_add(*now, n_hold);
+            }
+
+            return 0;
+        }
+    }
+
+    /* No existing entry, create a new one */
+    elt = malloc(sizeof(nib_lost_address_entry_t));
+
+    if (!elt) {
+        /* Insufficient memory */
+        return -1;
+    }
+
+    /* Increment usage counter of address in central NHDP address storage */
+    lost_addr->usg_count++;
+    elt->address = lost_addr;
+    elt->expiration_time = timex_add(*now, n_hold);
+    LL_PREPEND(nib_lost_address_entry_head, elt);
+
+    return 0;
+}
+
+/**
+ * Remove a given Lost Neighbor Tuple
+ */
+static void rem_ln_entry(nib_lost_address_entry_t *ln_entry)
+{
+    nhdp_decrement_addr_usage(ln_entry->address);
+    LL_DELETE(nib_lost_address_entry_head, ln_entry);
+    free(ln_entry);
+}
diff --git a/sys/net/routing/nhdp/nib_table.h b/sys/net/routing/nhdp/nib_table.h
new file mode 100644
index 0000000000..9971b729d1
--- /dev/null
+++ b/sys/net/routing/nhdp/nib_table.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     nhdp
+ * @{
+ *
+ * @file
+ * @brief       Neighbor Information Base interface for NHDP
+ *
+ * @author      Fabian Nack <nack@inf.fu-berlin.de>
+ */
+
+#ifndef NIB_TABLE_H_
+#define NIB_TABLE_H_
+
+#include "timex.h"
+
+#include "rfc5444/rfc5444_writer.h"
+
+#include "nhdp_address.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Neighbor Set entry (neighbor tuple)
+ */
+typedef struct nib_entry_t {
+    nhdp_addr_entry_t *address_list_head;   /**< Pointer to this tuple's addresses*/
+    uint8_t symmetric;                      /**< Flag whether sym link to this nb exists */
+    struct nib_entry_t *next;               /**< Pointer to next list entry */
+} nib_entry_t;
+
+/**
+ * @brief   Lost Neighbor Set entry (lost neighbor tuple, lnt)
+ */
+typedef struct nib_lost_address_entry_t {
+    nhdp_addr_t *address;                   /**< Pointer to addr represented by this lnt */
+    timex_t expiration_time;                /**< Time at which entry expires */
+    struct nib_lost_address_entry_t *next;  /**< Pointer to next list entry */
+} nib_lost_address_entry_t;
+
+/**
+ * @brief                   Process a received HELLO message in the NIB
+ *
+ * @note
+ * Must not be called from outside the NHDP reader's message processing.
+ *
+ * @param[in] nb_list       Pointer to the Neighbor Address List from the received HELLO
+ * @param[out] out_list     Pointer to the created Removed Address List
+ *
+ * @return                  Pointer to the new Neighbor Tuple
+ * @return                  NULL on error
+ */
+nib_entry_t *nib_process_hello(nhdp_addr_entry_t *nb_list, nhdp_addr_entry_t **out_list);
+
+/**
+ * @brief                   Add addresses to the currently constructed HELLO message
+ *
+ * @note
+ * Must not be called from outside the NHDP writer's message creation process.
+ *
+ * @param[in] wr            The NHDP writer used for message construction
+ */
+void nib_fill_wr_addresses(struct rfc5444_writer *wr);
+
+/**
+ * @brief                   Remove a Neighbor Tuple
+ *
+ * @param[in] nib_entry     Pointer to the Neighbor Tuple
+ */
+void nib_rem_nb_entry(nib_entry_t *nib_entry);
+
+/**
+ * @brief                   Set a Neighbor Tuple's symmetry flag
+ *
+ * Removes all Lost Neighbor Tuples representing addresses included in the
+ * Neighbor Tuple's address list.
+ *
+ * @param[in] nib_entry     Pointer to the Neighbor Tuple
+ */
+void nib_set_nb_entry_sym(nib_entry_t *nib_entry);
+
+/**
+ * @brief                   Reset a Neighbor Tuple's symmetry flag
+ *
+ * Adds a Lost Neighbor Tuple for every address in the Neighbor Tuple's
+ * address list.
+ *
+ * @param[in] nib_entry     Pointer to the Neighbor Tuple
+ * @param[in] now           Pointer to current time timex representation
+ */
+void nib_reset_nb_entry_sym(nib_entry_t *nib_entry, timex_t *now);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NIB_TABLE_H_ */
+/** @} */
-- 
GitLab