From 30c76adc438efed2f24339fab45b3fbc80967119 Mon Sep 17 00:00:00 2001
From: BytesGalore <Martin.Landsmann@HAW-Hamburg.de>
Date: Tue, 4 Nov 2014 08:47:57 +0100
Subject: [PATCH] net: added a core implementation of a FIB

---
 Makefile.dep                                  |   6 +
 sys/Makefile                                  |   3 +
 sys/Makefile.include                          |   4 +
 sys/include/net/ng_fib.h                      | 149 +++++
 sys/include/net/ng_fib/ng_fib_table.h         |  54 ++
 sys/include/net/ng_fib/ng_universal_address.h | 121 ++++
 sys/net/network_layer/fib/Makefile            |   1 +
 sys/net/network_layer/fib/fib.c               | 584 ++++++++++++++++++
 sys/net/network_layer/fib/universal_address.c | 261 ++++++++
 sys/shell/commands/Makefile                   |   3 +
 sys/shell/commands/sc_fib.c                   | 210 +++++++
 sys/shell/commands/shell_commands.c           |  11 +
 tests/unittests/tests-fib/Makefile            |   3 +
 tests/unittests/tests-fib/Makefile.include    |   1 +
 tests/unittests/tests-fib/tests-fib.c         | 560 +++++++++++++++++
 tests/unittests/tests-fib/tests-fib.h         |  43 ++
 16 files changed, 2014 insertions(+)
 create mode 100644 sys/include/net/ng_fib.h
 create mode 100644 sys/include/net/ng_fib/ng_fib_table.h
 create mode 100644 sys/include/net/ng_fib/ng_universal_address.h
 create mode 100644 sys/net/network_layer/fib/Makefile
 create mode 100644 sys/net/network_layer/fib/fib.c
 create mode 100644 sys/net/network_layer/fib/universal_address.c
 create mode 100644 sys/shell/commands/sc_fib.c
 create mode 100644 tests/unittests/tests-fib/Makefile
 create mode 100644 tests/unittests/tests-fib/Makefile.include
 create mode 100644 tests/unittests/tests-fib/tests-fib.c
 create mode 100644 tests/unittests/tests-fib/tests-fib.h

diff --git a/Makefile.dep b/Makefile.dep
index 8a90097714..0d8e61e00c 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -150,3 +150,9 @@ ifneq (,$(filter nhdp,$(USEMODULE)))
   USEMODULE += oonf_common
   USEMODULE += oonf_rfc5444
 endif
+
+ifneq (,$(filter fib,$(USEMODULE)))
+  USEMODULE += timex
+  USEMODULE += vtimer
+  USEMODULE += net_help
+endif
diff --git a/sys/Makefile b/sys/Makefile
index abf8bc9480..42d8c99c65 100644
--- a/sys/Makefile
+++ b/sys/Makefile
@@ -101,6 +101,9 @@ endif
 ifneq (,$(filter ng_pktdump,$(USEMODULE)))
     DIRS += net/crosslayer/ng_pktdump
 endif
+ifneq (,$(filter fib,$(USEMODULE)))
+    DIRS += net/network_layer/fib
+endif
 
 DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))
 
diff --git a/sys/Makefile.include b/sys/Makefile.include
index de8b66de52..ee40a88f82 100644
--- a/sys/Makefile.include
+++ b/sys/Makefile.include
@@ -74,6 +74,10 @@ ifneq (,$(filter oneway_malloc,$(USEMODULE)))
     USEMODULE_INCLUDES += $(RIOTBASE)/sys/oneway-malloc/include
 endif
 
+ifneq (,$(filter fib,$(USEMODULE)))
+    USEMODULE_INCLUDES += $(RIOTBASE)/sys/include/net
+endif
+
 ifneq (,$(filter embunit,$(USEMODULE)))
     ifeq ($(OUTPUT),XML)
         CFLAGS += -DOUTPUT=OUTPUT_XML
diff --git a/sys/include/net/ng_fib.h b/sys/include/net/ng_fib.h
new file mode 100644
index 0000000000..dbe6ae2cdc
--- /dev/null
+++ b/sys/include/net/ng_fib.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2014  Martin Landsmann <Martin.Landsmann@HAW-Hamburg.de>
+ *
+ * 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    net_fib Forwarding Information Base (FIB)
+ * @ingroup     net
+ * @brief       FIB implementation
+ *
+ * @{
+ *
+ * @file
+ * @brief       Types and functions for FIB
+ * @author      Martin Landsmann
+ */
+
+#ifndef FIB_H_
+#define FIB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Reactive Routing Protocol (RRP) message content to request/reply route discovery
+ */
+typedef struct rrp_address_msg_t {
+    uint8_t *address;      /**< The pointer to the address */
+    uint8_t address_size;  /**< The address size */
+    uint32_t address_flags; /**< The flags for the given address */
+} rrp_address_msg_t;
+
+#define FIB_MSG_RRP_SIGNAL (0x99)     /**< message type for RRP notifications */
+
+/**
+ * @brief indicator of a lifetime that does not expire (2^32 - 1)
+ */
+#define FIB_LIFETIME_NO_EXPIRE (0xFFFFFFFF)
+
+/**
+ * @brief initializes all FIB entries with 0
+ */
+void fib_init(void);
+
+/**
+ * @brief de-initializes the FIB entries
+ */
+void fib_deinit(void);
+
+/**
+ * @brief Registration of reactive routing protocol handler function
+ */
+void fib_register_rrp(void);
+
+/**
+ * @brief Adds a new entry in the corresponding FIB table for global destination and next hop
+ *
+ * @param[in] iface_id       the interface ID
+ * @param[in] dst            the destination address
+ * @param[in] dst_size       the destination address size
+ * @param[in] dst_flags      the destination address flags
+ * @param[in] next_hop       the next hop address to be updated
+ * @param[in] next_hop_size  the next hop address size
+ * @param[in] next_hop_flags the next-hop address flags
+ * @param[in] lifetime       the lifetime in ms to be updates
+ *
+ * @return 0 on success
+ *         -ENOMEM if the entry cannot be created due to unsufficient RAM
+ */
+int fib_add_entry(kernel_pid_t iface_id, uint8_t *dst, size_t dst_size, uint32_t dst_flags,
+                  uint8_t *next_hop, size_t next_hop_size, uint32_t next_hop_flags,
+                  uint32_t lifetime);
+
+/**
+ * @brief Updates an entry in the FIB table with next hop and lifetime
+ *
+ * @param[in] dst            the destination address
+ * @param[in] dst_size       the destination address size
+ * @param[in] next_hop       the next hop address to be updated
+ * @param[in] next_hop_size  the next hop address size
+ * @param[in] next_hop_flags the next-hop address flags
+ * @param[in] lifetime       the lifetime in ms to be updates
+ *
+ * @return 0 on success
+ *         -ENOMEM if the entry cannot be updated due to unsufficient RAM
+ */
+int fib_update_entry(uint8_t *dst, size_t dst_size,
+                     uint8_t *next_hop, size_t next_hop_size, uint32_t next_hop_flags,
+                     uint32_t lifetime);
+
+/**
+ * @brief removes an entry from the corresponding FIB table
+ *
+ * @param[in] dst       the destination address
+ * @param[in] dst_size  the destination address size
+ */
+void fib_remove_entry(uint8_t *dst, size_t dst_size);
+
+/**
+ * @brief provides a next hop for a given destination
+ *
+ * @param[in, out] iface_id       pointer to store the interface ID for the next hop
+ * @param[out] next_hop           pointer where the next hop address should be stored
+ * @param[in, out] next_hop_size  the next hop address size. The value is
+ *                                overwritten with the actual next hop size
+ * @param[out] next_hop_flags     the next-hop address flags, e.g. compression type
+ * @param[in] dst                 the destination address
+ * @param[in] dst_size            the destination address size
+ * @param[in] dst_flags           the destination address flags
+ *
+ * @return 0 on success
+ *         -EHOSTUNREACH if no next hop is available in any FIB table
+ *                                           all RRPs are notified before the return
+ *         -ENOBUFS if the size for the next hop address is insufficient low
+ */
+int fib_get_next_hop(kernel_pid_t *iface_id,
+                     uint8_t *next_hop, size_t *next_hop_size, uint32_t* next_hop_flags,
+                     uint8_t *dst, size_t dst_size, uint32_t dst_flags);
+
+/**
+ * @brief returns the actual number of used FIB entries
+ */
+int fib_get_num_used_entries(void);
+
+/**
+ * @brief Prints the kernel_pid_t for all registered RRPs
+ */
+void fib_print_notify_rrp(void);
+
+/**
+ * @brief Prints the FIB content (does not print the entries)
+ */
+void fib_print_fib_table(void);
+
+/**
+ * @brief Prints the FIB content
+ */
+void fib_print_routes(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FIB_H_ */
+/** @} */
diff --git a/sys/include/net/ng_fib/ng_fib_table.h b/sys/include/net/ng_fib/ng_fib_table.h
new file mode 100644
index 0000000000..4dae8e9bd9
--- /dev/null
+++ b/sys/include/net/ng_fib/ng_fib_table.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014  Martin Landsmann <Martin.Landsmann@HAW-Hamburg.de>
+ *
+ * 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.
+ */
+
+/**
+ * @brief       forwarding table structs
+ * @ingroup     net_fib
+ *
+ * @{
+ *
+ * @file
+ * @brief       Types and functions for operating fib tables
+ * @author      Martin Landsmann
+ */
+
+#ifndef FIB_TABLE_H_
+#define FIB_TABLE_H_
+
+#include <stdint.h>
+#include "vtimer.h"
+#include "ng_universal_address.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+* @brief Container descriptor for a FIB entry
+*/
+typedef struct fib_entry_t {
+    /** interface ID */
+    kernel_pid_t iface_id;
+    /** Lifetime of this entry */
+    timex_t lifetime;
+    /** Unique identifier for the type of the global address */
+    uint32_t global_flags;
+    /** Pointer to the shared generic address */
+    struct universal_address_container_t *global;
+    /** Unique identifier for the type of the next hop address */
+    uint32_t next_hop_flags;
+    /** Pointer to the shared generic address */
+    struct universal_address_container_t *next_hop;
+} fib_entry_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FIB_TABLE_H_ */
+/** @} */
diff --git a/sys/include/net/ng_fib/ng_universal_address.h b/sys/include/net/ng_fib/ng_universal_address.h
new file mode 100644
index 0000000000..30fecaadeb
--- /dev/null
+++ b/sys/include/net/ng_fib/ng_universal_address.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014  Martin Landsmann <Martin.Landsmann@HAW-Hamburg.de>
+ *
+ * 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    net_universal_address Universal Address Container
+ * @ingroup     net
+ * @brief       universal address container
+ *
+ * @{
+ *
+ * @file
+ * @brief       Types and functions for operating universal addresses
+ * @author      Martin Landsmann
+ */
+
+#ifndef UNIVERSAL_ADDRESS_H_
+#define UNIVERSAL_ADDRESS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UNIVERSAL_ADDRESS_SIZE (16)         /**< size of the used addresses in bytes        */
+
+/**
+ * @brief The container descriptor used to identify a universal address entry
+ */
+typedef struct universal_address_container_t {
+    uint8_t use_count;                       /**< The number of entries link here */
+    uint8_t address_size;                    /**< Size in bytes of the used genereic address */
+    uint8_t address[UNIVERSAL_ADDRESS_SIZE]; /**< the genereic address data */
+} universal_address_container_t;
+
+/**
+ * @brief initializes the datastructure for the entries
+ */
+void universal_address_init(void);
+
+/**
+ * @brief resets the usecoumt for all entries
+ */
+void universal_address_reset(void);
+
+/**
+ * @brief Adds a given address to the universal address entries
+ *        if the entry already exists, the use_count will be increased.
+ *
+ * @param[in] addr       pointer to the address
+ * @param[in] addr_size  the number of bytes required for the address entry
+ *
+ * @return pointer to the universal_address_container_t containing the address on success
+ *         NULL if the address could not be inserted
+ */
+universal_address_container_t *universal_address_add(uint8_t *addr, size_t addr_size);
+
+/**
+ * @brief Adds a given container from the universal address entries
+ *        if the entry exists, the use_count will be decreased.
+ *
+ * @param[in] entry  pointer to the universal_address_container_t to be removed
+ */
+void universal_address_rem(universal_address_container_t *entry);
+
+/**
+ * @brief copies the address from the given container to the provided pointer
+ *
+ * @param[in] entry          pointer to the universal_address_container_t
+ * @param[out] addr          pointer to store the address entry
+ * @param[in, out] addr_size pointer providing the size of available memory on addr
+ *                           this value is overwritten with the actual size required
+ *
+ * @return addr if the address is copied to the addr destination
+ *         NULL if the size is unsufficient for copy
+ */
+uint8_t* universal_address_get_address(universal_address_container_t *entry,
+                                       uint8_t *addr, size_t *addr_size);
+
+/**
+ * @brief determines if the entry equals the provided address
+ *
+ * @param[in] entry       pointer to the universal_address_container_t for compare
+ * @param[in] addr        pointer to the address for compare
+ * @param[in, out] addr_size   the number of bytes used for the address entry
+ *                             on sucessfull return this value is overwritten
+ *                             with the number of matching bytes till the
+ *                             first of trailing `0`s indicating a prefix
+ *
+ * @return 0 if the entries are equal or comperable
+ *         -ENOENT if the given adresses do not match
+ */
+int universal_address_compare(universal_address_container_t *entry,
+                              uint8_t *addr, size_t *addr_size);
+
+/**
+ * @brief Prints the content of the given entry
+ *
+ * @param[in] entry  pointer to the universal_address_container_t to be printed
+ */
+void universal_address_print_entry(universal_address_container_t *entry);
+
+/**
+ * @brief returns the number of used entries
+ */
+int universal_address_get_num_used_entries(void);
+
+/**
+ * @brief Prints the content of the genereic address table up to the used element
+ */
+void universal_address_print_table(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UNIVERSAL_ADDRESS_H_ */
+/** @} */
diff --git a/sys/net/network_layer/fib/Makefile b/sys/net/network_layer/fib/Makefile
new file mode 100644
index 0000000000..48422e909a
--- /dev/null
+++ b/sys/net/network_layer/fib/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/net/network_layer/fib/fib.c b/sys/net/network_layer/fib/fib.c
new file mode 100644
index 0000000000..00bc218587
--- /dev/null
+++ b/sys/net/network_layer/fib/fib.c
@@ -0,0 +1,584 @@
+/**
+ * FIB implementation
+ *
+ * Copyright (C) 2014 Martin Landsmann <Martin.Landsmann@HAW-Hamburg.de>
+ *
+ * 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 fib
+ * @{
+ * @file    fib.c
+ * @brief   Functions to manage FIB entries
+ * @author  Martin Landsmann <Martin.Landsmann@HAW-Hamburg.de>
+ * @}
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "thread.h"
+#include "mutex.h"
+#include "msg.h"
+
+#define ENABLE_DEBUG (0)
+#include "debug.h"
+
+#include "ng_fib.h"
+#include "ng_fib/ng_fib_table.h"
+
+/**
+ * @brief access mutex to control exclusive operations on calls
+ */
+static mutex_t mtx_access = MUTEX_INIT;
+
+/**
+ * @brief maximum number of handled reactice routing protocols (RRP)
+ *        used to notify the saved kernel_pid_t
+ */
+#define FIB_MAX_RRP (5)
+
+/**
+ * @brief registered RRPs for notification un unreachable destinations
+ */
+static size_t notify_rrp_pos = 0;
+
+/**
+ * @brief the kernel_pid_t for notifying the RRPs
+ */
+static kernel_pid_t notify_rrp[FIB_MAX_RRP];
+
+/**
+ * @brief maximum number of FIB tables entries handled
+ */
+#define FIB_MAX_FIB_TABLE_ENTRIES (20)
+
+/**
+ * @brief array of the FIB tables
+ */
+static fib_entry_t fib_table[FIB_MAX_FIB_TABLE_ENTRIES];
+
+/**
+ * @brief convert given ms to a timepoint from now on in the future
+ * @param[in] ms the miliseconds to be converted
+ * @param[out] timex the converted timepoint
+ */
+static void fib_ms_to_timex(uint32_t ms, timex_t *timex)
+{
+    vtimer_now(timex);
+    timex->seconds += ms / 1000;
+    timex->microseconds += (ms - timex->seconds * 1000) * 1000;
+}
+
+/**
+ * @brief returns pointer to the entry for the given destination address
+ *
+ * @param[in] dst                  the destination address
+ * @param[in] dst_size             the destination address size
+ * @param[out] entry_arr           the array to scribe the found match
+ * @param[in, out] entry_arr_size  the number of entries provided by entry_arr (should be always 1)
+ *                                 this value is overwritten with the actual found number
+ *
+ * @return 0 if we found a next-hop prefix
+ *         1 if we found the exact address next-hop
+ *         -EHOSTUNREACH if no fitting next-hop is available
+ */
+static int fib_find_entry(uint8_t *dst, size_t dst_size,
+                          fib_entry_t **entry_arr, size_t *entry_arr_size)
+{
+    timex_t now;
+    vtimer_now(&now);
+
+    size_t count = 0;
+    size_t prefix_size = 0;
+    size_t match_size = dst_size;
+    int ret = -EHOSTUNREACH;
+
+    for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) {
+
+        /* autoinvalidate if the entry lifetime is not set to not expire */
+        if ((fib_table[i].lifetime.seconds != FIB_LIFETIME_NO_EXPIRE)
+            || (fib_table[i].lifetime.microseconds != FIB_LIFETIME_NO_EXPIRE)) {
+
+            /* check if the lifetime expired */
+            if (timex_cmp(now, fib_table[i].lifetime) > -1) {
+                /* remove this entry if its lifetime expired */
+                fib_table[i].lifetime.seconds = 0;
+                fib_table[i].lifetime.microseconds = 0;
+
+                if (fib_table[i].global != NULL) {
+                    universal_address_rem(fib_table[i].global);
+                    fib_table[i].global = NULL;
+                }
+
+                if (fib_table[i].next_hop != NULL) {
+                    universal_address_rem(fib_table[i].next_hop);
+                    fib_table[i].next_hop = NULL;
+                }
+            }
+        }
+
+        if ((prefix_size < dst_size) &&
+            (fib_table[i].global != NULL) &&
+            (universal_address_compare(fib_table[i].global, dst, &match_size) == 0)) {
+
+            /* If we found an exact match */
+            if (match_size == dst_size) {
+                entry_arr[0] = &(fib_table[i]);
+                *entry_arr_size = 1;
+                /* we will not find a better one so we return */
+                return 1;
+            }
+            else {
+                /* we try to find the most fitting prefix */
+                if (match_size > prefix_size) {
+                    entry_arr[0] = &(fib_table[i]);
+                    /* we could find a better one so we move on */
+                    ret = 0;
+                }
+            }
+
+            prefix_size = match_size;
+            match_size = dst_size;
+            count = 1;
+        }
+    }
+
+    *entry_arr_size = count;
+    return ret;
+}
+
+/**
+ * @brief updates the next hop the lifetime and the interface id for a given entry
+ *
+ * @param[in] entry          the entry to be updated
+ * @param[in] next_hop       the next hop address to be updated
+ * @param[in] next_hop_size  the next hop address size
+ * @param[in] next_hop_flags the next-hop address flags
+ * @param[in] lifetime       the lifetime in ms
+ *
+ * @return 0 if the entry has been updated
+ *         -ENOMEM if the entry cannot be updated due to unsufficient RAM
+ */
+static int fib_upd_entry(fib_entry_t *entry,
+                         uint8_t *next_hop, size_t next_hop_size, uint32_t next_hop_flags,
+                         uint32_t lifetime)
+{
+    universal_address_container_t *container = universal_address_add(next_hop, next_hop_size);
+
+    if (container == NULL) {
+        return -ENOMEM;
+    }
+
+    universal_address_rem(entry->next_hop);
+    entry->next_hop = container;
+    entry->next_hop_flags = next_hop_flags;
+
+    if (lifetime < FIB_LIFETIME_NO_EXPIRE) {
+        fib_ms_to_timex(lifetime, &entry->lifetime);
+    }
+    else {
+        entry->lifetime.seconds = FIB_LIFETIME_NO_EXPIRE;
+        entry->lifetime.microseconds = FIB_LIFETIME_NO_EXPIRE;
+    }
+
+    return 0;
+}
+
+/**
+ * @brief creates a new FIB entry with the provided parameters
+ *
+ * @param[in] iface_id       the interface ID
+ * @param[in] dst            the destination address
+ * @param[in] dst_size       the destination address size
+ * @param[in] dst_flags      the destination address flags
+ * @param[in] next_hop       the next hop address
+ * @param[in] next_hop_size  the next hop address size
+ * @param[in] next_hop_flags the next-hop address flags
+ * @param[in] lifetime       the lifetime in ms
+ *
+ * @return 0 on success
+ *         -ENOMEM if no new entry can be created
+ */
+static int fib_create_entry(kernel_pid_t iface_id,
+                            uint8_t *dst, size_t dst_size, uint32_t dst_flags,
+                            uint8_t *next_hop, size_t next_hop_size, uint32_t next_hop_flags,
+                            uint32_t lifetime)
+{
+    for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) {
+        if (fib_table[i].lifetime.seconds == 0 && fib_table[i].lifetime.microseconds == 0) {
+
+            fib_table[i].global = universal_address_add(dst, dst_size);
+
+            if (fib_table[i].global != NULL) {
+                fib_table[i].global_flags = dst_flags;
+                fib_table[i].next_hop = universal_address_add(next_hop, next_hop_size);
+                fib_table[i].next_hop_flags = next_hop_flags;
+            }
+
+            if (fib_table[i].next_hop != NULL) {
+                /* everything worked fine */
+                fib_table[i].iface_id = iface_id;
+
+                if (lifetime < FIB_LIFETIME_NO_EXPIRE) {
+                    fib_ms_to_timex(lifetime, &fib_table[i].lifetime);
+                }
+                else {
+                    fib_table[i].lifetime.seconds = FIB_LIFETIME_NO_EXPIRE;
+                    fib_table[i].lifetime.microseconds = FIB_LIFETIME_NO_EXPIRE;
+                }
+
+                return 0;
+            }
+        }
+    }
+
+    return -ENOMEM;
+}
+
+/**
+ * @brief removes the given entry
+ *
+ * @param[in] entry the entry to be removed
+ *
+ * @return 0 on success
+ */
+static int fib_remove(fib_entry_t *entry)
+{
+    if (entry->global != NULL) {
+        universal_address_rem(entry->global);
+    }
+
+    if (entry->next_hop) {
+        universal_address_rem(entry->next_hop);
+    }
+
+    entry->global = NULL;
+    entry->global_flags = 0;
+    entry->next_hop = NULL;
+    entry->next_hop_flags = 0;
+
+    entry->iface_id = KERNEL_PID_UNDEF;
+    entry->lifetime.seconds = 0;
+    entry->lifetime.microseconds = 0;
+
+    return 0;
+}
+
+/**
+ * @brief signalls (sends a message to) all registered RRPs
+ *        to start a route discovery for the provided destination.
+ *        The receiver MUST copy the content, i.e. the address before reply.
+ *
+ * @param[in] dst       the destination address
+ * @param[in] dst_size  the destination address size
+ *
+ * @return 0 on a new available entry,
+ *         -ENOENT if no suiting entry is provided.
+ */
+static int fib_signal_rrp(uint8_t *dst, size_t dst_size, uint32_t dst_flags)
+{
+    msg_t msg, reply;
+    rrp_address_msg_t content;
+    content.address = dst;
+    content.address_size = dst_size;
+    content.address_flags = dst_flags;
+    int ret = -ENOENT;
+
+    msg.type = FIB_MSG_RRP_SIGNAL;
+    msg.content.ptr = (void *)&content;
+
+    for (size_t i = 0; i < FIB_MAX_RRP; ++i) {
+        if (notify_rrp[i] != KERNEL_PID_UNDEF) {
+            DEBUG("[fib_signal_rrp] send msg@: %p to pid[%d]: %d\n", \
+                  msg.content.ptr, (int)i, (int)notify_rrp[i]);
+
+            /* the receiver, i.e. the RRP, MUST copy the content value.
+             * using the provided pointer after replying this message
+             * will lead to errors
+             */
+            msg_send_receive(&msg, &reply, notify_rrp[i]);
+            DEBUG("[fib_signal_rrp] got reply.");
+        }
+    }
+
+    return ret;
+}
+
+int fib_add_entry(kernel_pid_t iface_id, uint8_t *dst, size_t dst_size, uint32_t dst_flags,
+                  uint8_t *next_hop, size_t next_hop_size, uint32_t next_hop_flags,
+                  uint32_t lifetime)
+{
+    mutex_lock(&mtx_access);
+    DEBUG("[fib_add_entry]");
+    size_t count = 1;
+    fib_entry_t *entry[count];
+
+    int ret = fib_find_entry(dst, dst_size, &(entry[0]), &count);
+
+    if (ret == 1) {
+        /* we must take the according entry and update the values */
+        ret = fib_upd_entry(entry[0], next_hop, next_hop_size, next_hop_flags, lifetime);
+    }
+    else {
+        ret = fib_create_entry(iface_id, dst, dst_size, dst_flags,
+                               next_hop, next_hop_size, next_hop_flags, lifetime);
+    }
+
+    mutex_unlock(&mtx_access);
+    return ret;
+}
+
+int fib_update_entry(uint8_t *dst, size_t dst_size,
+                     uint8_t *next_hop, size_t next_hop_size, uint32_t next_hop_flags,
+                     uint32_t lifetime)
+{
+    mutex_lock(&mtx_access);
+    DEBUG("[fib_update_entry]");
+    size_t count = 1;
+    fib_entry_t *entry[count];
+    int ret = -ENOMEM;
+
+    if (fib_find_entry(dst, dst_size, &(entry[0]), &count) == 1) {
+        DEBUG("[fib_update_entry] found entry: %p\n", (void *)(entry[0]));
+        /* we must take the according entry and update the values */
+        ret = fib_upd_entry(entry[0], next_hop, next_hop_size, next_hop_flags, lifetime);
+    }
+    else {
+        /* we have ambigious entries, i.e. count > 1
+         * this should never happen
+         */
+        DEBUG("[fib_update_entry] ambigious entries detected!!!");
+    }
+
+    mutex_unlock(&mtx_access);
+    return ret;
+}
+
+void fib_remove_entry(uint8_t *dst, size_t dst_size)
+{
+    mutex_lock(&mtx_access);
+    DEBUG("[fib_remove_entry]");
+    size_t count = 1;
+    fib_entry_t *entry[count];
+
+    int ret = fib_find_entry(dst, dst_size, &(entry[0]), &count);
+
+    if (ret == 1) {
+        /* we must take the according entry and update the values */
+        fib_remove(entry[0]);
+    }
+    else {
+        /* we have ambigious entries, i.e. count > 1
+         * this should never happen
+         */
+        DEBUG("[fib_update_entry] ambigious entries detected!!!");
+    }
+
+    mutex_unlock(&mtx_access);
+}
+
+int fib_get_next_hop(kernel_pid_t *iface_id,
+                     uint8_t *next_hop, size_t *next_hop_size, uint32_t *next_hop_flags,
+                     uint8_t *dst, size_t dst_size, uint32_t dst_flags)
+{
+    mutex_lock(&mtx_access);
+    DEBUG("[fib_get_next_hop]");
+    size_t count = 1;
+    fib_entry_t *entry[count];
+
+    int ret = fib_find_entry(dst, dst_size, &(entry[0]), &count);
+
+    if (!(ret == 0 || ret == 1)) {
+        /* notify all RRPs for route discovery if available */
+        if (fib_signal_rrp(dst, dst_size, dst_flags) == 0) {
+            count = 1;
+            /* now lets see if the RRPs have found a valid next-hop */
+            ret = fib_find_entry(dst, dst_size, &(entry[0]), &count);
+        }
+    }
+
+    if (ret == 0 || ret == 1) {
+
+        uint8_t *address_ret = universal_address_get_address(entry[0]->next_hop,
+                               next_hop, next_hop_size);
+
+        if (address_ret == NULL) {
+            mutex_unlock(&mtx_access);
+            return -ENOBUFS;
+        }
+    }
+    else {
+        mutex_unlock(&mtx_access);
+        return -EHOSTUNREACH;
+    }
+
+    *iface_id = entry[0]->iface_id;
+    *next_hop_flags = entry[0]->next_hop_flags;
+    mutex_unlock(&mtx_access);
+    return 0;
+}
+
+void fib_init(void)
+{
+    DEBUG("[fib_init] hello. Initializing some stuff.");
+    mutex_lock(&mtx_access);
+
+    for (size_t i = 0; i < FIB_MAX_RRP; ++i) {
+        notify_rrp[i] = KERNEL_PID_UNDEF;
+    }
+
+    for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) {
+        fib_table[i].iface_id = 0;
+        fib_table[i].lifetime.seconds = 0;
+        fib_table[i].lifetime.microseconds = 0;
+        fib_table[i].global_flags = 0;
+        fib_table[i].global = NULL;
+        fib_table[i].next_hop_flags = 0;
+        fib_table[i].next_hop = NULL;
+    }
+
+    universal_address_init();
+    mutex_unlock(&mtx_access);
+}
+
+void fib_deinit(void)
+{
+    DEBUG("[fib_deinit] hello. De-Initializing stuff.");
+    mutex_lock(&mtx_access);
+
+    for (size_t i = 0; i < FIB_MAX_RRP; ++i) {
+        notify_rrp[i] = KERNEL_PID_UNDEF;
+    }
+
+    notify_rrp_pos = 0;
+
+    for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) {
+        fib_table[i].iface_id = 0;
+        fib_table[i].lifetime.seconds = 0;
+        fib_table[i].lifetime.microseconds = 0;
+        fib_table[i].global_flags = 0;
+        fib_table[i].global = NULL;
+        fib_table[i].next_hop_flags = 0;
+        fib_table[i].next_hop = NULL;
+    }
+
+    universal_address_reset();
+    mutex_unlock(&mtx_access);
+}
+
+void fib_register_rrp(void)
+{
+    mutex_lock(&mtx_access);
+
+    if (notify_rrp_pos < FIB_MAX_RRP) {
+        notify_rrp[notify_rrp_pos] = sched_active_pid;
+        notify_rrp_pos++;
+    }
+
+    mutex_unlock(&mtx_access);
+}
+
+int fib_get_num_used_entries(void)
+{
+    mutex_lock(&mtx_access);
+    size_t used_entries = 0;
+
+    for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) {
+        used_entries += (size_t)(fib_table[i].global != NULL);
+    }
+
+    mutex_unlock(&mtx_access);
+    return used_entries;
+}
+
+/* print functions */
+
+void fib_print_notify_rrp(void)
+{
+    mutex_lock(&mtx_access);
+
+    for (size_t i = 0; i < FIB_MAX_RRP; ++i) {
+        printf("[fib_print_notify_rrp] pid[%d]: %d\n", (int)i, (int)notify_rrp[i]);
+    }
+
+    mutex_unlock(&mtx_access);
+}
+
+void fib_print_fib_table(void)
+{
+    mutex_lock(&mtx_access);
+
+    for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) {
+        printf("[fib_print_fib_table] %d) iface_id: %d, global: %p, next hop: %p, lifetime: %d.%d\n", \
+               (int)i, \
+               (int)fib_table[i].iface_id, \
+               (void *)fib_table[i].global, \
+               (void *)fib_table[i].next_hop, \
+               (int)fib_table[i].lifetime.seconds, \
+               (int)fib_table[i].lifetime.microseconds);
+    }
+
+    mutex_unlock(&mtx_access);
+}
+
+static void fib_print_adress(universal_address_container_t *entry)
+{
+    uint8_t address[UNIVERSAL_ADDRESS_SIZE];
+    size_t addr_size = UNIVERSAL_ADDRESS_SIZE;
+    uint8_t *ret = universal_address_get_address(entry, address, &addr_size);
+
+    if (ret == address) {
+        for (size_t i = 0; i < UNIVERSAL_ADDRESS_SIZE; ++i) {
+            if (i <= addr_size) {
+                printf("%02x", address[i]);
+            }
+            else {
+                printf("  ");
+            }
+        }
+    }
+}
+
+void fib_print_routes(void)
+{
+    mutex_lock(&mtx_access);
+    printf("%-32s %-6s %-32s %-6s %-16s Interface\n"
+           , "Destination", "Flags", "Next Hop", "Flags", "Expires");
+
+    timex_t now;
+    vtimer_now(&now);
+
+    for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) {
+        if (fib_table[i].lifetime.seconds != 0 || fib_table[i].lifetime.microseconds != 0) {
+            fib_print_adress(fib_table[i].global);
+            printf(" 0x%04x ", fib_table[i].global_flags);
+            fib_print_adress(fib_table[i].next_hop);
+            printf(" 0x%04x ", fib_table[i].next_hop_flags);
+
+            if ((fib_table[i].lifetime.seconds != FIB_LIFETIME_NO_EXPIRE)
+                || (fib_table[i].lifetime.microseconds != FIB_LIFETIME_NO_EXPIRE)) {
+
+                timex_t tm = timex_sub(fib_table[i].lifetime, now);
+
+                /* we must interpret the values as signed */
+                if ((int32_t)tm.seconds < 0
+                    || (tm.seconds == 0 && (int32_t)tm.microseconds < 0)) {
+                    printf("%-16s ", "EXPIRED");
+                }
+                else {
+                    printf("%"PRIu32".%05"PRIu32, tm.seconds, tm.microseconds);
+                }
+            }
+            else {
+                printf("%-16s ", "NEVER");
+            }
+
+            printf("%d\n", (int)fib_table[i].iface_id);
+        }
+    }
+
+    mutex_unlock(&mtx_access);
+}
diff --git a/sys/net/network_layer/fib/universal_address.c b/sys/net/network_layer/fib/universal_address.c
new file mode 100644
index 0000000000..d1d817479e
--- /dev/null
+++ b/sys/net/network_layer/fib/universal_address.c
@@ -0,0 +1,261 @@
+/**
+ * universal address container implementation
+ *
+ * Copyright (C) 2014 Martin Landsmann <Martin.Landsmann@HAW-Hamburg.de>
+ *
+ * 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 fib
+ * @{
+ * @file    universal_address.c
+ * @brief   Functions to manage universal address container
+ * @author  Martin Landsmann <Martin.Landsmann@HAW-Hamburg.de>
+ * @}
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "mutex.h"
+
+#define ENABLE_DEBUG (0)
+#include "debug.h"
+#include "ng_fib/ng_universal_address.h"
+
+/**
+ * @brief Maximum number of entries handled
+ */
+#define UNIVERSAL_ADDRESS_MAX_ENTRIES (40)
+
+/**
+ * @brief counter indicating the number of entries allocated
+ */
+static size_t universal_address_table_filled = 0;
+
+/**
+ * @brief The array of universal_address containers
+ */
+static universal_address_container_t universal_address_table[UNIVERSAL_ADDRESS_MAX_ENTRIES];
+
+/**
+ * @brief access mutex to control exclusive operations on calls
+ */
+static mutex_t mtx_access = MUTEX_INIT;
+
+/**
+ * @brief finds the universal address container for the given address
+ *
+ * @param[in] addr       pointer to the address
+ * @param[in] addr_size  the number of bytes required for the address entry
+ *
+ * @return pointer to the universal_address_container_t containing the address on success
+ *         NULL if the address could not be inserted
+ */
+static universal_address_container_t *universal_address_find_entry(uint8_t *addr, size_t addr_size)
+{
+    for (size_t i = 0; i < UNIVERSAL_ADDRESS_MAX_ENTRIES; ++i) {
+        if ((universal_address_table[i].address_size == addr_size) && (universal_address_table[i].address != NULL)) {
+            if (memcmp((universal_address_table[i].address), addr, addr_size) == 0) {
+                return &(universal_address_table[i]);
+            }
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * @brief finds the next empty or unused universal address containers
+ *
+ * @return pointer to the next free/unused universal_address_container_t
+ *         or NULL if no memory is left in universal_address_table
+ */
+static universal_address_container_t *universal_address_get_next_unused_entry(void)
+{
+    if (universal_address_table_filled < UNIVERSAL_ADDRESS_MAX_ENTRIES) {
+        for (size_t i = 0; i < UNIVERSAL_ADDRESS_MAX_ENTRIES; ++i) {
+            if ((universal_address_table[i].use_count == 0)) {
+                return &(universal_address_table[i]);
+            }
+        }
+    }
+
+    return NULL;
+}
+
+universal_address_container_t *universal_address_add(uint8_t *addr, size_t addr_size)
+{
+    mutex_lock(&mtx_access);
+    universal_address_container_t *pEntry = universal_address_find_entry(addr, addr_size);
+
+    if (pEntry == NULL) {
+        /* look for a free entry */
+        pEntry = universal_address_get_next_unused_entry();
+
+        if (pEntry == NULL) {
+            mutex_unlock(&mtx_access);
+            /* no free room */
+            return NULL;
+        }
+
+        /* look if the former memory has distinct size */
+        if (pEntry->address_size != addr_size) {
+            /* clean the address */
+            memset(pEntry->address, 0, UNIVERSAL_ADDRESS_SIZE);
+
+            /* set the used bytes */
+            pEntry->address_size = addr_size;
+            pEntry->use_count = 0;
+        }
+
+        /* copy the address */
+        memcpy((pEntry->address), addr, addr_size);
+    }
+
+    pEntry->use_count++;
+
+    if (pEntry->use_count == 1) {
+        DEBUG("[universal_address_add] universal_address_table_filled: %d\n", \
+              (int)universal_address_table_filled);
+        universal_address_table_filled++;
+    }
+
+    mutex_unlock(&mtx_access);
+    return pEntry;
+}
+
+void universal_address_rem(universal_address_container_t *entry)
+{
+    mutex_lock(&mtx_access);
+    DEBUG("[universal_address_rem] entry: %p\n", (void *)entry);
+
+    /* we do not delete anything on remove */
+    if (entry != NULL) {
+        if (entry->use_count != 0) {
+            entry->use_count--;
+
+            if (entry->use_count == 0) {
+                universal_address_table_filled--;
+            }
+        }
+        else {
+            DEBUG("[universal_address_rem] universal_address_table_filled: %d\n", \
+                  (int)universal_address_table_filled);
+        }
+    }
+
+    mutex_unlock(&mtx_access);
+}
+
+uint8_t* universal_address_get_address(universal_address_container_t *entry,
+                                  uint8_t *addr, size_t *addr_size)
+{
+    mutex_lock(&mtx_access);
+
+    if (*addr_size >= entry->address_size) {
+        memcpy(addr, entry->address, entry->address_size);
+        *addr_size = entry->address_size;
+        mutex_unlock(&mtx_access);
+        return addr;
+    }
+
+    *addr_size = entry->address_size;
+    mutex_unlock(&mtx_access);
+    return NULL;
+}
+
+int universal_address_compare(universal_address_container_t *entry,
+                              uint8_t *addr, size_t *addr_size)
+{
+    mutex_lock(&mtx_access);
+
+    int ret = -ENOENT;
+
+    /* If we have distinct sizes, the addresses are probably not comperable */
+    if (entry->address_size != *addr_size) {
+        mutex_unlock(&mtx_access);
+        return ret;
+    }
+
+    /* Get the index of the first trailing `0` (indicates a network prefix) */
+    int i = 0;
+    for( i = entry->address_size-1; i >= 0; --i) {
+        if( entry->address[i] != 0 ) {
+            break;
+        }
+    }
+
+    if( memcmp(entry->address, addr, i+1) == 0 ) {
+        ret = 0;
+        *addr_size = i+1;
+    }
+
+    mutex_unlock(&mtx_access);
+    return ret;
+}
+
+void universal_address_init(void)
+{
+    mutex_lock(&mtx_access);
+
+    for (size_t i = 0; i < UNIVERSAL_ADDRESS_MAX_ENTRIES; ++i) {
+        universal_address_table[i].use_count = 0;
+        universal_address_table[i].address_size = 0;
+        memset(universal_address_table[i].address, 0, UNIVERSAL_ADDRESS_SIZE);
+    }
+
+    mutex_unlock(&mtx_access);
+}
+
+void universal_address_reset(void)
+{
+    mutex_lock(&mtx_access);
+
+    for (size_t i = 0; i < UNIVERSAL_ADDRESS_MAX_ENTRIES; ++i) {
+        universal_address_table[i].use_count = 0;
+    }
+
+    universal_address_table_filled = 0;
+    mutex_unlock(&mtx_access);
+}
+
+void universal_address_print_entry(universal_address_container_t *entry)
+{
+    mutex_lock(&mtx_access);
+
+    if (entry != NULL) {
+        printf("[universal_address_print_entry] entry@: %p, use_count: %d, \
+address_size: %d, content: ", \
+               (void *)entry, (int)entry->use_count, (int)entry->address_size);
+
+        for (size_t i = 0; i < entry->address_size; ++i) {
+            /* printf("%02x ", (char)entry->address[i]); */
+            printf("%c", (char)entry->address[i]);
+        }
+
+        puts("");
+    }
+
+    mutex_unlock(&mtx_access);
+}
+
+int universal_address_get_num_used_entries(void)
+{
+    mutex_lock(&mtx_access);
+    size_t ret = universal_address_table_filled;
+    mutex_unlock(&mtx_access);
+    return ret;
+}
+
+void universal_address_print_table(void)
+{
+    printf("[universal_address_print_table] universal_address_table_filled: %d\n", \
+           (int)universal_address_table_filled);
+
+    for (size_t i = 0; i < UNIVERSAL_ADDRESS_MAX_ENTRIES; ++i) {
+        universal_address_print_entry((&universal_address_table[i]));
+    }
+}
diff --git a/sys/shell/commands/Makefile b/sys/shell/commands/Makefile
index 68ca2308b4..a86ce8bc80 100644
--- a/sys/shell/commands/Makefile
+++ b/sys/shell/commands/Makefile
@@ -58,6 +58,9 @@ endif
 ifneq (,$(filter ng_netif,$(USEMODULE)))
   SRC += sc_netif.c
 endif
+ifneq (,$(filter fib,$(USEMODULE)))
+	SRC += sc_fib.c
+endif
 
 # TODO
 # Conditional building not possible at the moment due to
diff --git a/sys/shell/commands/sc_fib.c b/sys/shell/commands/sc_fib.c
new file mode 100644
index 0000000000..c68e0aaaa0
--- /dev/null
+++ b/sys/shell/commands/sc_fib.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2015 Martin Landsmann <Martin.Landsmann@HAW-Hamburg.de>
+ *
+ * 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     sys_shell_commands
+ * @{
+ *
+ * @file
+ * @brief       Provides shell commands to manage and show FIB Entries
+ *
+ * @author      2015 Martin Landsmann <Martin.Landsmann@HAW-Hamburg.de>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include "thread.h"
+#include "inet_pton.h"
+#include "ng_fib.h"
+
+#define INFO1_TXT "fibroute add <destination> via <next hop> dev <device>"
+#define INFO2_TXT " [lifetime <lifetime>]"
+#define INFO3_TXT "       <destination> - the destination address\n" \
+                  "       <next hop>    - the address of the next-hop towards the <destination>\n" \
+                  "       <device>      - the device id of the Interface to use\n"
+#define INFO4_TXT "       <lifetime>    - optional lifetime in ms when the entry automatically invalidates\n"
+#define INFO5_TXT "fibroute del <destination>\n" \
+                  "       <destination> - the destination address of the entry to be deleted\n"
+
+static unsigned char tmp_ipv4_dst[INADDRSZ];  /**< buffer for ipv4 address conversion */
+static unsigned char tmp_ipv4_nxt[INADDRSZ];  /**< buffer for ipv4 address conversion */
+static unsigned char tmp_ipv6_dst[IN6ADDRSZ]; /**< buffer for ipv6 address conversion */
+static unsigned char tmp_ipv6_nxt[IN6ADDRSZ]; /**< buffer for ipv6 address conversion */
+
+static void _fib_usage(int info)
+{
+    switch (info) {
+        case 1: {
+            puts("\nbrief: adds a new entry to the FIB.\nusage: "
+                 INFO1_TXT "\n" INFO3_TXT);
+            break;
+        }
+
+        case 2: {
+            puts("\nbrief: adds a new entry to the FIB.\nusage: "
+                 INFO1_TXT INFO2_TXT "\n" INFO3_TXT INFO4_TXT);
+            break;
+        }
+
+        case 3: {
+            puts("\nbrief: deletes an entry from the FIB.\nusage: "
+                 INFO5_TXT);
+            break;
+        }
+
+        default:
+            break;
+    };
+}
+
+void _fib_route_handler(int argc, char **argv)
+{
+    /* e.g. fibroute right now dont care about the adress/protocol family */
+    if (argc == 1) {
+        fib_print_routes();
+        return;
+    }
+
+    /* e.g. firoute [add|del] */
+    if (argc == 2) {
+        if ((strcmp("add", argv[1]) == 0)) {
+            _fib_usage(2);
+        }
+        else if ((strcmp("del", argv[1]) == 0)) {
+            _fib_usage(3);
+        }
+        else {
+            puts("\nunrecognized parameter1.\nPlease enter fibroute [add|del] for more information.");
+        }
+
+        return;
+    }
+
+    if (argc > 2 && !((strcmp("add", argv[1]) == 0) || (strcmp("del", argv[1]) == 0))) {
+        puts("\nunrecognized parameter2.\nPlease enter fibroute [add|del] for more information.");
+        return;
+    }
+
+    /* e.g. fibroute del <destination> */
+    if (argc == 3) {
+        if (inet_pton(AF_INET6, argv[2], tmp_ipv6_dst)) {
+            fib_remove_entry(tmp_ipv6_dst, IN6ADDRSZ);
+        }
+        else if (inet_pton(AF_INET, argv[2], tmp_ipv4_dst)) {
+            fib_remove_entry(tmp_ipv4_dst, INADDRSZ);
+        }
+        else {
+            fib_remove_entry((uint8_t *)argv[2], (strlen(argv[2])));
+        }
+
+        return;
+    }
+
+    /* e.g. fibroute add <destination> via <next hop> dev <device> */
+    if (argc == 7) {
+        if ((strcmp("add", argv[1]) == 0) && (strcmp("via", argv[3]) == 0)
+            && (strcmp("dev", argv[5]) == 0)) {
+
+            unsigned char *dst = (unsigned char *)argv[2];
+            size_t dst_size = (strlen(argv[2]));
+            uint32_t dst_flags = 0xffff;
+            unsigned char *nxt = (unsigned char *)argv[4];
+            size_t nxt_size = (strlen(argv[4]));
+            uint32_t nxt_flags = 0xffff;
+
+            /* determine destination address */
+            if (inet_pton(AF_INET6, argv[2], tmp_ipv6_dst)) {
+                dst = tmp_ipv6_dst;
+                dst_size = IN6ADDRSZ;
+                dst_flags = AF_INET6;
+            }
+            else if (inet_pton(AF_INET, argv[2], tmp_ipv4_dst)) {
+                dst = tmp_ipv4_dst;
+                dst_size = INADDRSZ;
+                dst_flags = AF_INET;
+            }
+
+            /* determine next-hop address */
+            if (inet_pton(AF_INET6, argv[4], tmp_ipv6_nxt)) {
+                nxt = tmp_ipv6_nxt;
+                nxt_size = IN6ADDRSZ;
+                nxt_flags = AF_INET6;
+            }
+            else if (inet_pton(AF_INET, argv[4], tmp_ipv4_nxt)) {
+                nxt = tmp_ipv4_nxt;
+                nxt_size = INADDRSZ;
+                nxt_flags = AF_INET;
+            }
+
+            fib_add_entry((kernel_pid_t) atoi(argv[6]),
+                          dst, dst_size, dst_flags,
+                          nxt, nxt_size, nxt_flags,
+                          FIB_LIFETIME_NO_EXPIRE);
+        }
+        else {
+            _fib_usage(1);
+        }
+
+        return;
+    }
+
+    /* e.g. fibroute add <destination> via <next hop> dev <device> lifetime <lifetime> */
+    if (argc == 9) {
+        if ((strcmp("add", argv[1]) == 0) && (strcmp("via", argv[3]) == 0)
+            && (strcmp("dev", argv[5]) == 0)
+            && (strcmp("lifetime", argv[7]) == 0)) {
+
+            unsigned char *dst = (unsigned char *)argv[2];
+            size_t dst_size = (strlen(argv[2]));
+            uint32_t dst_flags = 0xffff;
+            unsigned char *nxt = (unsigned char *)argv[4];
+            size_t nxt_size = (strlen(argv[4]));
+            uint32_t nxt_flags = 0xffff;
+
+            /* determine destination address */
+            if (inet_pton(AF_INET6, argv[2], tmp_ipv6_dst)) {
+                dst = tmp_ipv6_dst;
+                dst_size = IN6ADDRSZ;
+                dst_flags = AF_INET6;
+            }
+            else if (inet_pton(AF_INET, argv[2], tmp_ipv4_dst)) {
+                dst = tmp_ipv4_dst;
+                dst_size = INADDRSZ;
+                dst_flags = AF_INET;
+            }
+
+            /* determine next-hop address */
+            if (inet_pton(AF_INET6, argv[4], tmp_ipv6_nxt)) {
+                nxt = tmp_ipv6_nxt;
+                nxt_size = IN6ADDRSZ;
+                nxt_flags = AF_INET6;
+            }
+            else if (inet_pton(AF_INET, argv[4], tmp_ipv4_nxt)) {
+                nxt = tmp_ipv4_nxt;
+                nxt_size = INADDRSZ;
+                nxt_flags = AF_INET;
+            }
+
+            fib_add_entry((kernel_pid_t) atoi(argv[6]),
+                          dst, dst_size, dst_flags,
+                          nxt, nxt_size, nxt_flags,
+                          (uint32_t)atoi(argv[8]));
+        }
+        else {
+            _fib_usage(2);
+        }
+
+        return;
+    }
+
+    puts("\nunrecognized parameters.\nPlease enter fibroute [add|del] for more information.");
+}
diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c
index dd87a9aa32..0cc4c779b5 100644
--- a/sys/shell/commands/shell_commands.c
+++ b/sys/shell/commands/shell_commands.c
@@ -159,6 +159,10 @@ extern int _netif_send(int argc, char **argv);
 #endif
 #endif
 
+#ifdef MODULE_FIB
+extern void _fib_route_handler( int argc, char **argv );
+#endif
+
 const shell_command_t _shell_command_list[] = {
     {"reboot", "Reboot the node", _reboot_handler},
 #ifdef MODULE_CONFIG
@@ -259,6 +263,13 @@ const shell_command_t _shell_command_list[] = {
 #ifndef MODULE_TRANSCEIVER
     {"txtsnd", "send raw data", _netif_send },
 #endif
+#endif
+#ifdef MODULE_FIB
+    {
+        "fibroute",
+        "Manipulate the FIB (info: 'fibroute [add|del]')",
+        _fib_route_handler
+    },
 #endif
     {NULL, NULL, NULL}
 };
diff --git a/tests/unittests/tests-fib/Makefile b/tests/unittests/tests-fib/Makefile
new file mode 100644
index 0000000000..cc35beb967
--- /dev/null
+++ b/tests/unittests/tests-fib/Makefile
@@ -0,0 +1,3 @@
+MODULE = tests-fib
+
+include $(RIOTBASE)/Makefile.base
diff --git a/tests/unittests/tests-fib/Makefile.include b/tests/unittests/tests-fib/Makefile.include
new file mode 100644
index 0000000000..7b57b02e44
--- /dev/null
+++ b/tests/unittests/tests-fib/Makefile.include
@@ -0,0 +1 @@
+USEMODULE += fib
diff --git a/tests/unittests/tests-fib/tests-fib.c b/tests/unittests/tests-fib/tests-fib.c
new file mode 100644
index 0000000000..407adaa0d0
--- /dev/null
+++ b/tests/unittests/tests-fib/tests-fib.c
@@ -0,0 +1,560 @@
+/*
+* Copyright (C) 2015 Martin Landsmann
+*
+* 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.
+*/
+
+#define TEST_FIB_SHOW_OUTPUT (0) /**< set  */
+
+#include <stdio.h> /**< required for snprintf() */
+#include <string.h>
+#include <errno.h>
+#include "embUnit.h"
+#include "tests-fib.h"
+
+#include "thread.h"
+#include "ng_fib.h"
+#include "ng_fib/ng_universal_address.h"
+
+/*
+* @brief helper to fill FIB with unique entries
+*/
+static void _fill_FIB_unique(size_t entries)
+{
+    size_t add_buf_size = 16;
+    char addr_dst[add_buf_size];
+    char addr_nxt[add_buf_size];
+    uint32_t addr_dst_flags = 0x77777777;
+    uint32_t addr_nxt_flags = 0x77777777;
+
+    for (size_t i = 0; i < entries; ++i) {
+        /* construct "addresses" for the FIB */
+        snprintf(addr_dst, add_buf_size, "Test address %02d", (int)i);
+        snprintf(addr_nxt, add_buf_size, "Test address %02d", entries + i);
+        /* the terminating \0 is unnecessary here */
+        fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, addr_dst_flags,
+                      (uint8_t *)addr_nxt, add_buf_size - 1, addr_nxt_flags, 10000);
+    }
+}
+
+/*
+* @brief helper to fill FIB with multiple used entries
+* The modulus adjusts the number of reused addresses
+*/
+static void _fill_FIB_multiple(size_t entries, size_t modulus)
+{
+    size_t add_buf_size = 16;
+    char addr_dst[add_buf_size];
+    char addr_nxt[add_buf_size];
+    uint32_t addr_dst_flags = 0x33333333;
+    uint32_t addr_nxt_flags = 0x33333333;
+
+    for (size_t i = 0; i < entries; ++i) {
+        /* construct "addresses" for the FIB */
+        snprintf(addr_dst, add_buf_size, "Test address %02d", (int)i);
+        snprintf(addr_nxt, add_buf_size, "Test address %02d", i % modulus);
+        fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, addr_dst_flags,
+                      (uint8_t *)addr_nxt, add_buf_size - 1, addr_nxt_flags, 10000);
+    }
+}
+
+/*
+* @brief filling the FIB with entries
+* It is expected to have 20 FIB entries and 40 used universal address entries
+*/
+static void test_fib_01_fill_unique_entries(void)
+{
+    _fill_FIB_unique(20);
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(40, universal_address_get_num_used_entries());
+    fib_deinit();
+}
+
+/*
+ * @brief filling the FIB with reusable entries
+ * It is expected to have 20 FIB entries and 20 universal address entries
+ */
+static void test_fib_02_fill_multiple_entries(void)
+{
+    size_t entries = 20;
+    _fill_FIB_multiple(entries, 11);
+
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries());
+    fib_deinit();
+}
+
+/*
+* @brief filling the FIB with entries and removing all entries
+* It is expected to have 0 FIB entries and 0 universal address entries after remove
+*/
+static void test_fib_03_removing_all_entries(void)
+{
+    size_t add_buf_size = 16;
+    char addr_dst[add_buf_size];
+
+    size_t entries = 20;
+    _fill_FIB_unique(entries);
+
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(40, universal_address_get_num_used_entries());
+
+    for (size_t i = 0; i < entries; ++i) {
+        /* construct "addresses" to remove */
+        snprintf(addr_dst, add_buf_size, "Test address %02d", (int)i);
+        fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1);
+    }
+
+    TEST_ASSERT_EQUAL_INT(0, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(0, universal_address_get_num_used_entries());
+
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+
+    fib_deinit();
+}
+
+/*
+* @brief filling the FIB with entries and removing the lower 1/2 entries (0..9)
+* It is expected to have 10 FIB entries and 19 used universal address entries after remove
+*/
+static void test_fib_04_remove_lower_half(void)
+{
+    size_t add_buf_size = 16;
+    char addr_dst[add_buf_size];
+
+    size_t entries = 20;
+    _fill_FIB_multiple(entries, 11);
+
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries());
+
+    for (size_t i = 0; i < entries / 2; ++i) {
+        /* construct "addresses" to remove */
+        snprintf(addr_dst, add_buf_size, "Test address %02d", (int)i);
+        fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1);
+    }
+
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+    TEST_ASSERT_EQUAL_INT(10, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(19, universal_address_get_num_used_entries());
+    fib_deinit();
+}
+
+/*
+* @brief filling the FIB with entries and removing the upper 1/2 entries (10..19)
+* It is expected to have 10 FIB entries and 10 universal address entries after remove
+*/
+static void test_fib_05_remove_upper_half(void)
+{
+    size_t add_buf_size = 16;
+    char addr_dst[add_buf_size];
+
+    size_t entries = 20;
+    _fill_FIB_multiple(entries, 11);
+
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries());
+
+    for (size_t i = 0; i < entries / 2; ++i) {
+        /* construct "addresses" to remove */
+        snprintf(addr_dst, add_buf_size, "Test address %02d", ((entries / 2) + i));
+        fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1);
+    }
+
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+    TEST_ASSERT_EQUAL_INT(10, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(10, universal_address_get_num_used_entries());
+    fib_deinit();
+}
+
+/*
+* @brief filling the FIB with entries and removing one entry
+* It is expected to have 19 FIB entries and still 20 universal address entries
+* after removing 02
+* (the use count for 02 is reduced to 1 after remove)
+*/
+static void test_fib_06_remove_one_entry(void)
+{
+    size_t add_buf_size = 16;
+    char addr_dst[] = "Test address 02";
+
+    size_t entries = 20;
+    _fill_FIB_multiple(entries, 11);
+
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries());
+
+    fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1);
+
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+    TEST_ASSERT_EQUAL_INT(19, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries());
+    fib_deinit();
+}
+
+/*
+* @brief filling the FIB with entries and removing one entry several times
+* It is expected to have 19 FIB entries and 19 universal address entries
+* after removing 13
+*/
+static void test_fib_07_remove_one_entry_multiple_times(void)
+{
+    size_t add_buf_size = 16; /* includes space for terminating \0 */
+    char addr_dst[] = "Test address 13";
+
+    size_t entries = 20;
+    _fill_FIB_multiple(entries, 11);
+
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries());
+
+    fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1);
+    fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1);
+    fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1);
+    fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1);
+
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+    TEST_ASSERT_EQUAL_INT(19, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(19, universal_address_get_num_used_entries());
+    fib_deinit();
+}
+
+/*
+* @brief filling the FIB with entries and removing an unknown entry
+* It is expected to have 20 FIB entries and 20 universal address entries after removing
+*/
+static void test_fib_08_remove_unknown(void)
+{
+    size_t add_buf_size = 16; /* includes space for terminating \0 */
+    char addr_dst[] = "Test address 99";
+
+    size_t entries = 20;
+    _fill_FIB_multiple(entries, 11);
+
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries());
+
+    fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1);
+    fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1);
+    fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1);
+
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries());
+    fib_deinit();
+}
+
+/*
+* @brief filling the FIB with entries and update an entry
+* It is expected to have FIB entry 13 with updated lifetime of 9999
+* and entry 7 with updated iface ID of 7, lifetime of 7777 and next hop "Test address 77"
+*/
+static void test_fib_09_update_entry(void)
+{
+    size_t add_buf_size = 16; /* includes space for terminating \0 */
+    char addr_dst13[] = "Test address 13";
+    char addr_dst07[] = "Test address 07";
+    char addr_nxt2[] = "Test address 99";
+    char addr_nxt77[] = "Test address 77";
+
+    size_t entries = 20;
+    _fill_FIB_multiple(entries, 11);
+
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries());
+
+    fib_update_entry((uint8_t *)addr_dst13, add_buf_size - 1,
+                     (uint8_t *)addr_nxt2, add_buf_size - 1, 0x99, 9999);
+    fib_update_entry((uint8_t *)addr_dst07, add_buf_size - 1,
+                     (uint8_t *)addr_nxt77, add_buf_size - 1, 0x77, 7777);
+
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+    fib_deinit();
+}
+
+/*
+* @brief filling the FIB with entries and adding an additional one (not fitting)
+* It is expected to have 20 FIB entries and to receive FPC_ERROR on adding an additional one
+*/
+static void test_fib_10_add_exceed(void)
+{
+    size_t add_buf_size = 16; /* includes space for terminating \0 */
+    char addr_dst[] = "Test address 98";
+    char addr_nxt[] = "Test address 99";
+
+    size_t entries = 20;
+    _fill_FIB_unique(entries);
+
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(40, universal_address_get_num_used_entries());
+
+    int ret = fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, 0x98,
+                            (uint8_t *)addr_nxt, add_buf_size - 1, 0x99, 9999);
+
+    TEST_ASSERT_EQUAL_INT(-ENOMEM, ret);
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(40, universal_address_get_num_used_entries());
+
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+    fib_deinit();
+}
+
+/*
+* @brief get next hop for known destination
+* It is expected to get the next hop 02 and receive 0
+*/
+static void test_fib_11_get_next_hop_success(void)
+{
+    size_t add_buf_size = 16; /* includes space for terminating \0 */
+    char addr_dst[] = "Test address 13";
+    char addr_expect[] = "Test address 02";
+    kernel_pid_t iface_id = KERNEL_PID_UNDEF;
+    uint32_t next_hop_flags = 0;
+    char addr_nxt[add_buf_size];
+
+    size_t entries = 20;
+    _fill_FIB_multiple(entries, 11);
+
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries());
+    int ret = fib_get_next_hop(&iface_id,
+                               (uint8_t *)addr_nxt, &add_buf_size, &next_hop_flags,
+                               (uint8_t *)addr_dst, add_buf_size - 1, 0x13);
+
+    TEST_ASSERT_EQUAL_INT(0, ret);
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries());
+
+    ret = strncmp(addr_expect, addr_nxt, add_buf_size);
+    TEST_ASSERT_EQUAL_INT(0, ret);
+
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+    fib_deinit();
+}
+
+/*
+* @brief get next hop for unknown destination
+* It is expected to get no next hop and receive -EHOSTUNREACH
+*/
+static void test_fib_12_get_next_hop_fail(void)
+{
+    size_t add_buf_size = 16; /* includes space for terminating \0 */
+    char addr_dst[] = "Test address 99";
+    kernel_pid_t iface_id = KERNEL_PID_UNDEF;
+    uint32_t next_hop_flags = 0;
+    char addr_nxt[add_buf_size];
+
+    size_t entries = 20;
+    _fill_FIB_multiple(entries, 11);
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries());
+    int ret = fib_get_next_hop(&iface_id,
+                               (uint8_t *)addr_nxt, &add_buf_size, &next_hop_flags,
+                               (uint8_t *)addr_dst, add_buf_size - 1, 0x99);
+
+    TEST_ASSERT_EQUAL_INT(-EHOSTUNREACH, ret);
+
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+    fib_deinit();
+}
+
+/*
+* @brief get next hop for known destination but unsufficient size for the output
+* It is expected to get no next hop and receive -ENOBUFS
+*/
+static void test_fib_13_get_next_hop_fail_on_buffer_size(void)
+{
+    size_t add_buf_size = 16; /* includes space for terminating \0 */
+    char addr_dst[] = "Test address 13";
+    kernel_pid_t iface_id = KERNEL_PID_UNDEF;
+    uint32_t next_hop_flags = 0;
+    size_t add_buf_size_nxt = 12;
+    char addr_nxt[add_buf_size];
+
+    size_t entries = 20;
+    _fill_FIB_multiple(entries, 11);
+    TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries());
+    TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries());
+
+    int ret = fib_get_next_hop(&iface_id,
+                               (uint8_t *)addr_nxt, &add_buf_size_nxt, &next_hop_flags,
+                               (uint8_t *)addr_dst, add_buf_size - 1, 0x13);
+
+    TEST_ASSERT_EQUAL_INT(-ENOBUFS, ret);
+    TEST_ASSERT_EQUAL_INT(add_buf_size_nxt, add_buf_size - 1);
+
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+    fib_deinit();
+}
+
+/*
+* @brief testing prefix and exact match
+* It is expected receive 23 for addr123 as exact match and
+* 12 for addr124
+*/
+static void test_fib_14_exact_and_prefix_match(void)
+{
+    size_t add_buf_size = 16;
+    char addr_dst[add_buf_size];
+    char addr_nxt[add_buf_size];
+    kernel_pid_t iface_id = KERNEL_PID_UNDEF;
+    uint32_t next_hop_flags = 0;
+    char addr_lookup[add_buf_size];
+    memset(addr_dst, 0, add_buf_size);
+    memset(addr_nxt, 0, add_buf_size);
+
+    snprintf(addr_dst, add_buf_size, "Test addr12");
+    snprintf(addr_nxt, add_buf_size, "Test address %02d", 12);
+    fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, 0x12,
+                  (uint8_t *)addr_nxt, add_buf_size - 1, 0x12, 100000);
+
+    snprintf(addr_dst, add_buf_size, "Test addr123");
+    snprintf(addr_nxt, add_buf_size, "Test address %02d", 23);
+    fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, 0x123,
+                  (uint8_t *)addr_nxt, add_buf_size - 1, 0x23, 100000);
+
+    snprintf(addr_dst, add_buf_size, "Test addr1234");
+    snprintf(addr_nxt, add_buf_size, "Test address %02d", 34);
+    fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, 0x1234,
+                  (uint8_t *)addr_nxt, add_buf_size - 1, 0x34, 100000);
+
+    /* exact match */
+    snprintf(addr_lookup, add_buf_size, "Test addr123");
+    int ret = fib_get_next_hop(&iface_id,
+                               (uint8_t *)addr_nxt, &add_buf_size, &next_hop_flags,
+                               (uint8_t *)addr_lookup, add_buf_size - 1, 0x123);
+
+    TEST_ASSERT_EQUAL_INT(0, ret);
+
+    char addr_expect_01[] = "Test address 23";
+    ret = strncmp(addr_expect_01, addr_nxt, add_buf_size - 1);
+    TEST_ASSERT_EQUAL_INT(0, ret);
+
+    /* prefix match */
+    add_buf_size = 16;
+    memset(addr_nxt, 0, add_buf_size);
+    memset(addr_lookup, 0, add_buf_size);
+
+    /* cppcheck: addr_lookup is only passed but not required to be read,
+    *            since we test prefix matching
+    */
+    /* cppcheck-suppress redundantCopy */
+    snprintf(addr_lookup, add_buf_size, "Test addr124");
+    ret = fib_get_next_hop(&iface_id,
+                           (uint8_t *)addr_nxt, &add_buf_size, &next_hop_flags,
+                           (uint8_t *)addr_lookup, add_buf_size - 1, 0x124);
+
+    TEST_ASSERT_EQUAL_INT(0, ret);
+
+    add_buf_size = 16;
+    char addr_expect_02[] = "Test address 12";
+    ret = strncmp(addr_expect_02, addr_nxt, add_buf_size);
+    TEST_ASSERT_EQUAL_INT(0, ret);
+
+#if (TEST_FIB_SHOW_OUTPUT == 1)
+    fib_print_fib_table();
+    puts("");
+    universal_address_print_table();
+    puts("");
+#endif
+    fib_deinit();
+}
+
+Test *tests_fib_tests(void)
+{
+    fib_init();
+    EMB_UNIT_TESTFIXTURES(fixtures) {
+        new_TestFixture(test_fib_01_fill_unique_entries),
+                        new_TestFixture(test_fib_02_fill_multiple_entries),
+                        new_TestFixture(test_fib_03_removing_all_entries),
+                        new_TestFixture(test_fib_04_remove_lower_half),
+                        new_TestFixture(test_fib_05_remove_upper_half),
+                        new_TestFixture(test_fib_06_remove_one_entry),
+                        new_TestFixture(test_fib_07_remove_one_entry_multiple_times),
+                        new_TestFixture(test_fib_08_remove_unknown),
+                        new_TestFixture(test_fib_09_update_entry),
+                        new_TestFixture(test_fib_10_add_exceed),
+                        new_TestFixture(test_fib_11_get_next_hop_success),
+                        new_TestFixture(test_fib_12_get_next_hop_fail),
+                        new_TestFixture(test_fib_13_get_next_hop_fail_on_buffer_size),
+                        new_TestFixture(test_fib_14_exact_and_prefix_match),
+    };
+
+    EMB_UNIT_TESTCALLER(fib_tests, NULL, NULL, fixtures);
+
+    return (Test *)&fib_tests;
+}
+
+void tests_fib(void)
+{
+    TESTS_RUN(tests_fib_tests());
+}
diff --git a/tests/unittests/tests-fib/tests-fib.h b/tests/unittests/tests-fib/tests-fib.h
new file mode 100644
index 0000000000..59f906d31a
--- /dev/null
+++ b/tests/unittests/tests-fib/tests-fib.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 Martin Landsmann
+ *
+ * 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.
+ */
+
+/**
+ * @addtogroup  unittests
+ * @{
+ *
+ * @file        tests-fib.h
+ * @brief       Unittests for the ``fib`` module
+ *
+ * @author      Martin Landsmann <Martin.Landsmann@HAW-Hamburg.de>
+ */
+#ifndef TESTS_FIB_H_
+#define TESTS_FIB_H_
+#include "embUnit/embUnit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+*  @brief   The entry point of this test suite.
+*/
+void tests_fib(void);
+
+/**
+ * @brief   Generates tests for FIB
+ *
+ * @return  embUnit tests if successful, NULL if not.
+ */
+Test *tests_fib_tests(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TESTS_FIB_H_ */
+/** @} */
-- 
GitLab