From 0c67c02047f8430baa50de38f1f9fcc3e8c781cf Mon Sep 17 00:00:00 2001
From: Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
Date: Fri, 12 Sep 2014 16:12:26 +0000
Subject: [PATCH] Add the AODVv2 Routing Protocol

This PR depends on #1766.

It contains a minimal implementation of the AODVv2 routing protocol.
*Not* implemented are:

	- AckReqs
	- alternate metrics
	- multiple interfaces
	- clients and Client Networks
	- buffering
	- all addresses, TLVs, and features that are marked as optional

An example application can be found at https://github.com/Lotterleben/RIOT-AODVv2/tree/master/aodvv2_demo.

The implementation relies heavily on a functioning Neighbor Discovery Protocol.
It might be necessary to fill the neighbor cache manually with the current state
of RIOTs NDP implementation.

The value of AODVV2_MAX_UNREACHABLE_NODES has been chosen arbitrarily and will be subject to
future improvement.

Please note that based on my experience, with the default transceiver
buffer size (3) of the native port, about 2/3 of the route discoveries
will fail. This has been addressed in issue #1747. It is advised to increase
the transceiver buffer size when using AODVv2 as a routing protocol.
---
 Makefile.dep                          |   7 +
 sys/Makefile                          |   3 +
 sys/net/include/aodvv2/aodvv2.h       |  52 ++
 sys/net/include/aodvv2/types.h        | 102 ++++
 sys/net/routing/aodvv2/Makefile       |   1 +
 sys/net/routing/aodvv2/aodv.c         | 478 ++++++++++++++++
 sys/net/routing/aodvv2/aodv.h         | 131 +++++
 sys/net/routing/aodvv2/aodv_debug.h   |  54 ++
 sys/net/routing/aodvv2/constants.h    |  61 +++
 sys/net/routing/aodvv2/reader.c       | 754 ++++++++++++++++++++++++++
 sys/net/routing/aodvv2/reader.h       |  62 +++
 sys/net/routing/aodvv2/routingtable.c | 268 +++++++++
 sys/net/routing/aodvv2/routingtable.h | 163 ++++++
 sys/net/routing/aodvv2/seqnum.c       |  49 ++
 sys/net/routing/aodvv2/seqnum.h       |  63 +++
 sys/net/routing/aodvv2/utils.c        | 242 +++++++++
 sys/net/routing/aodvv2/utils.h        | 115 ++++
 sys/net/routing/aodvv2/writer.c       | 359 ++++++++++++
 sys/net/routing/aodvv2/writer.h       | 101 ++++
 19 files changed, 3065 insertions(+)
 create mode 100644 sys/net/include/aodvv2/aodvv2.h
 create mode 100644 sys/net/include/aodvv2/types.h
 create mode 100644 sys/net/routing/aodvv2/Makefile
 create mode 100644 sys/net/routing/aodvv2/aodv.c
 create mode 100644 sys/net/routing/aodvv2/aodv.h
 create mode 100644 sys/net/routing/aodvv2/aodv_debug.h
 create mode 100644 sys/net/routing/aodvv2/constants.h
 create mode 100644 sys/net/routing/aodvv2/reader.c
 create mode 100644 sys/net/routing/aodvv2/reader.h
 create mode 100644 sys/net/routing/aodvv2/routingtable.c
 create mode 100644 sys/net/routing/aodvv2/routingtable.h
 create mode 100644 sys/net/routing/aodvv2/seqnum.c
 create mode 100644 sys/net/routing/aodvv2/seqnum.h
 create mode 100644 sys/net/routing/aodvv2/utils.c
 create mode 100644 sys/net/routing/aodvv2/utils.h
 create mode 100644 sys/net/routing/aodvv2/writer.c
 create mode 100644 sys/net/routing/aodvv2/writer.h

diff --git a/Makefile.dep b/Makefile.dep
index ed6742868f..63a105a39a 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -47,6 +47,13 @@ ifneq (,$(filter sixlowpan,$(USEMODULE)))
 	USEMODULE += vtimer
 endif
 
+ifneq (,$(filter aodvv2,$(USEMODULE)))
+        USEMODULE += vtimer
+        USEMODULE += sixlowpan
+        USEMODULE += oonf_common
+        USEMODULE += oonf_rfc5444
+endif
+
 ifneq (,$(filter uart0,$(USEMODULE)))
 	USEMODULE += posix
 endif
diff --git a/sys/Makefile b/sys/Makefile
index 2a8f2210b8..6e12c3770e 100644
--- a/sys/Makefile
+++ b/sys/Makefile
@@ -44,6 +44,9 @@ endif
 ifneq (,$(filter routing,$(USEMODULE)))
 	DIRS += net/routing
 endif
+ifneq (,$(filter aodvv2,$(USEMODULE)))
+    DIRS += net/routing/aodvv2
+endif
 ifneq (,$(filter ieee802154,$(USEMODULE)))
     DIRS += net/link_layer/ieee802154
 endif
diff --git a/sys/net/include/aodvv2/aodvv2.h b/sys/net/include/aodvv2/aodvv2.h
new file mode 100644
index 0000000000..86b6b03290
--- /dev/null
+++ b/sys/net/include/aodvv2/aodvv2.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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    aodvv2 AODVv2
+ * @brief       The Ad-hoc On-demand Distance Vector routing protocol, version 2
+ * @ingroup     net
+ * @{
+ *
+ * @file        aodvv2/aodvv2.h
+ * @brief       Interface for the AODVv2 routing protocol
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#ifndef AODVV2_H_
+#define AODVV2_H_
+
+#include "common/netaddr.h"
+#include "rfc5444/rfc5444_print.h"
+
+#include "aodvv2/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Initialize the AODVv2 routing protocol.
+ */
+void aodv_init(void);
+
+/**
+ * @brief   Set the metric type. If metric_type does not match any known metric
+ *          types, no changes will be made.
+ *
+ * @param[in] metric_type       type of new metric
+ */
+void aodv_set_metric_type(aodvv2_metric_t metric_type);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* AODVV2_H_ */
+/** @} */
diff --git a/sys/net/include/aodvv2/types.h b/sys/net/include/aodvv2/types.h
new file mode 100644
index 0000000000..47de03dd63
--- /dev/null
+++ b/sys/net/include/aodvv2/types.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        aodvv2/types.h
+ * @brief       data types for the aodvv2 routing protocol
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#ifndef AODVV2_TYPES_H
+#define AODVV2_TYPES_H
+
+#include "common/netaddr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   AODVv2 metric types. Extend to include alternate metrics.
+ */
+typedef enum {
+    HOP_COUNT = 3,              /**< see RFC6551*/
+} aodvv2_metric_t;
+
+typedef uint16_t aodvv2_seqnum_t;
+
+#define AODVV2_DEFAULT_METRIC_TYPE HOP_COUNT
+
+/**
+ * @brief   AODVv2 message types
+ */
+enum rfc5444_msg_type
+{
+    RFC5444_MSGTYPE_RREQ = 10,
+    RFC5444_MSGTYPE_RREP = 11,
+    RFC5444_MSGTYPE_RERR = 12,
+};
+
+/**
+ * @brief   AODVv2 TLV types
+ */
+enum rfc5444_tlv_type
+{
+    RFC5444_MSGTLV_ORIGSEQNUM,
+    RFC5444_MSGTLV_TARGSEQNUM,
+    RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM,
+    RFC5444_MSGTLV_METRIC,
+};
+
+/**
+ * @brief   Data about an OrigNode or TargNode, typically embedded in an
+ *          aodvv2_packet_data struct.
+ */
+struct node_data
+{
+    struct netaddr addr;                /**< IP address of the node */
+    uint8_t metric;                     /**< Metric value */
+    aodvv2_seqnum_t seqnum;             /**< Sequence Number */
+};
+
+/**
+ * @brief   all data contained in a RREQ or RREP.
+ */
+struct aodvv2_packet_data
+{
+    uint8_t hoplimit;                   /**< Hop limit */
+    struct netaddr sender;              /**< IP address of the neighboring router
+                                         *   which sent the RREQ/RREP*/
+    aodvv2_metric_t metricType;         /**< Metric type */
+    struct node_data origNode;          /**< Data about the originating node */
+    struct node_data targNode;          /**< Data about the originating node */
+    timex_t timestamp;                  /**< point at which the packet was (roughly)
+                                         *   received. Note that this timestamp
+                                         *   will be set after the packet has been
+                                         *   successfully parsed. */
+};
+
+/**
+ * @brief   Data about an unreachable node to be embedded in a RERR.
+ */
+struct unreachable_node
+{
+    struct netaddr addr;                        /**< IP address */
+    aodvv2_seqnum_t seqnum;                     /**< Sequence Number */
+};
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* AODVV2_TYPES_H */
diff --git a/sys/net/routing/aodvv2/Makefile b/sys/net/routing/aodvv2/Makefile
new file mode 100644
index 0000000000..48422e909a
--- /dev/null
+++ b/sys/net/routing/aodvv2/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/net/routing/aodvv2/aodv.c b/sys/net/routing/aodvv2/aodv.c
new file mode 100644
index 0000000000..47f216333e
--- /dev/null
+++ b/sys/net/routing/aodvv2/aodv.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        aodv.c
+ * @brief       aodvv2 routing protocol
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#include "debug.h"
+
+#include "aodv.h"
+#include "aodvv2/aodvv2.h"
+#include "aodv_debug.h"
+
+#define ENABLE_DEBUG (0)
+
+#define UDP_BUFFER_SIZE     (128) /** with respect to IEEE 802.15.4's MTU */
+#define RCV_MSG_Q_SIZE      (32)  /* TODO: check if smaller values work, too */
+
+static void _init_addresses(void);
+static void _init_sock_snd(void);
+static void *_aodv_receiver_thread(void *arg);
+static void *_aodv_sender_thread(void *arg);
+static void _deep_free_msg_container(struct msg_container *msg_container);
+static void _write_packet(struct rfc5444_writer *wr __attribute__ ((unused)),
+                          struct rfc5444_writer_target *iface __attribute__((unused)),
+                          void *buffer, size_t length);
+
+#ifdef DEBUG_ENABLED
+char addr_str[IPV6_MAX_ADDR_STR_LEN];
+static struct netaddr_str nbuf;
+#endif
+
+static char aodv_rcv_stack_buf[KERNEL_CONF_STACKSIZE_MAIN];
+static char aodv_snd_stack_buf[KERNEL_CONF_STACKSIZE_MAIN];
+
+static aodvv2_metric_t _metric_type;
+static int sender_thread;
+static int _sock_snd;
+static struct autobuf _hexbuf;
+static sockaddr6_t sa_wp;
+static ipv6_addr_t _v6_addr_local, _v6_addr_mcast, _v6_addr_loopback;
+static struct netaddr na_local; /* the same as _v6_addr_local, but to save us
+                                 * constant calls to ipv6_addr_t_to_netaddr()... */
+static struct writer_target *wt;
+struct netaddr na_mcast = (struct netaddr){};
+
+void aodv_init(void)
+{
+    AODV_DEBUG("%s()\n", __func__);
+
+    /* TODO: set if_id properly */
+    int if_id = 0;
+    net_if_set_src_address_mode(if_id, NET_IF_TRANS_ADDR_M_SHORT);
+
+    aodv_set_metric_type(AODVV2_DEFAULT_METRIC_TYPE);
+    _init_addresses();
+    _init_sock_snd();
+
+    /* init ALL the things! \o, */
+    seqnum_init();
+    routingtable_init();
+    clienttable_init();
+
+    /* every node is its own client. */
+    clienttable_add_client(&na_local);
+    rreqtable_init();
+
+    /* init reader and writer */
+    aodv_packet_reader_init();
+    aodv_packet_writer_init(_write_packet);
+
+    /* start listening & enable sending */
+    thread_create(aodv_rcv_stack_buf, sizeof(aodv_rcv_stack_buf), PRIORITY_MAIN,
+                  CREATE_STACKTEST, _aodv_receiver_thread, NULL, "_aodv_receiver_thread");
+    AODV_DEBUG("listening on port %d\n", HTONS(MANET_PORT));
+    sender_thread = thread_create(aodv_snd_stack_buf, sizeof(aodv_snd_stack_buf),
+                                  PRIORITY_MAIN, CREATE_STACKTEST, _aodv_sender_thread,
+                                  NULL, "_aodv_sender_thread");
+
+    /* register aodv for routing */
+    ipv6_iface_set_routing_provider(aodv_get_next_hop);
+
+}
+
+void aodv_set_metric_type(aodvv2_metric_t metric_type)
+{
+    if (metric_type != AODVV2_DEFAULT_METRIC_TYPE) {
+        return;
+    }
+    _metric_type = metric_type;
+}
+
+void aodv_send_rreq(struct aodvv2_packet_data *packet_data)
+{
+    AODV_DEBUG("%s()\n", __func__);
+
+    struct aodvv2_packet_data *pd = malloc(sizeof(struct aodvv2_packet_data));
+    memcpy(pd, packet_data, sizeof(struct aodvv2_packet_data));
+
+    struct rreq_rrep_data *rd = malloc(sizeof(struct rreq_rrep_data));
+    *rd = (struct rreq_rrep_data) {
+        .next_hop = &na_mcast,
+        .packet_data = pd,
+    };
+
+    struct msg_container *mc = malloc(sizeof(struct msg_container));
+    *mc = (struct msg_container) {
+        .type = RFC5444_MSGTYPE_RREQ,
+        .data = rd
+    };
+
+    msg_t msg;
+    msg.content.ptr = (char *) mc;
+
+    msg_try_send(&msg, sender_thread);
+}
+
+void aodv_send_rrep(struct aodvv2_packet_data *packet_data, struct netaddr *next_hop)
+{
+    AODV_DEBUG("%s()\n", __func__);
+
+    struct aodvv2_packet_data *pd = malloc(sizeof(struct aodvv2_packet_data));
+    memcpy(pd, packet_data, sizeof(struct aodvv2_packet_data));
+
+    struct netaddr *nh = malloc(sizeof(struct netaddr));
+    memcpy(nh, next_hop, sizeof(struct netaddr));
+
+    struct rreq_rrep_data *rd = malloc(sizeof(struct rreq_rrep_data));
+    *rd = (struct rreq_rrep_data) {
+        .next_hop = nh,
+        .packet_data = pd,
+    };
+
+    struct msg_container *mc = malloc(sizeof(struct msg_container));
+    *mc = (struct msg_container) {
+        .type = RFC5444_MSGTYPE_RREP,
+        .data = rd
+    };
+
+    msg_t msg;
+    msg.content.ptr = (char *) mc;
+
+    msg_try_send(&msg, sender_thread);
+}
+
+void aodv_send_rerr(struct unreachable_node unreachable_nodes[], size_t len, struct netaddr *next_hop)
+{
+    AODV_DEBUG("%s()\n", __func__);
+
+    struct rerr_data *rerrd = malloc(sizeof(struct rerr_data));
+    *rerrd = (struct rerr_data) {
+        .unreachable_nodes = unreachable_nodes,
+        .len = len,
+        .hoplimit = AODVV2_MAX_HOPCOUNT,
+        .next_hop = next_hop
+    };
+
+    struct msg_container *mc2 = malloc(sizeof(struct msg_container));
+    *mc2 = (struct msg_container) {
+        .type = RFC5444_MSGTYPE_RERR,
+        .data = rerrd
+    };
+
+    msg_t msg2;
+    msg2.content.ptr = (char *) mc2;
+
+    msg_try_send(&msg2, sender_thread);
+}
+
+/*
+ * init the multicast address all RREQ and RERRS are sent to
+ * and the local address (source address) of this node
+ */
+static void _init_addresses(void)
+{
+    /* init multicast address: set to to a link-local all nodes multicast address */
+    ipv6_addr_set_all_nodes_addr(&_v6_addr_mcast);
+    AODV_DEBUG("my multicast address is: %s\n",
+          ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &_v6_addr_mcast));
+
+    /* get best IP for sending */
+    ipv6_net_if_get_best_src_addr(&_v6_addr_local, &_v6_addr_mcast);
+    AODV_DEBUG("my src address is:       %s\n",
+          ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &_v6_addr_local));
+
+    /* store src & multicast address as netaddr as well for easy interaction
+     * with oonf based stuff */
+    ipv6_addr_t_to_netaddr(&_v6_addr_local, &na_local);
+    ipv6_addr_t_to_netaddr(&_v6_addr_mcast, &na_mcast);
+    ipv6_addr_set_loopback_addr(&_v6_addr_loopback);
+
+    /* init sockaddr that write_packet will use to send data */
+    sa_wp.sin6_family = AF_INET6;
+    sa_wp.sin6_port = HTONS(MANET_PORT);
+}
+
+/* init socket communication for sender */
+static void _init_sock_snd(void)
+{
+    _sock_snd = socket_base_socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+
+    if (-1 == _sock_snd) {
+        AODV_DEBUG("Error Creating Socket!\n");
+    }
+}
+
+/* Build RREQs, RREPs and RERRs from the information contained in the thread's
+ * message queue and send them */
+static void *_aodv_sender_thread(void *arg)
+{
+    (void) arg;
+
+    msg_t msgq[RCV_MSG_Q_SIZE];
+    msg_init_queue(msgq, sizeof msgq);
+    AODV_DEBUG("_aodv_sender_thread initialized.\n");
+
+    while (true) {
+        AODV_DEBUG("%s()\n", __func__);
+        msg_t msg;
+        msg_receive(&msg);
+        struct msg_container *mc = (struct msg_container *) msg.content.ptr;
+
+        if (mc->type == RFC5444_MSGTYPE_RREQ) {
+            struct rreq_rrep_data *rreq_data = (struct rreq_rrep_data *) mc->data;
+            aodv_packet_writer_send_rreq(rreq_data->packet_data, rreq_data->next_hop);
+        }
+        else if (mc->type == RFC5444_MSGTYPE_RREP) {
+            struct rreq_rrep_data *rrep_data = (struct rreq_rrep_data *) mc->data;
+            aodv_packet_writer_send_rrep(rrep_data->packet_data, rrep_data->next_hop);
+        }
+        else if (mc->type == RFC5444_MSGTYPE_RERR) {
+            struct rerr_data *rerr_data = (struct rerr_data *) mc->data;
+            aodv_packet_writer_send_rerr(rerr_data->unreachable_nodes, rerr_data->len,
+                             rerr_data->hoplimit, rerr_data->next_hop);
+        }
+        else {
+            DEBUG("ERROR: Couldn't identify Message\n");
+        }
+        _deep_free_msg_container(mc);
+    }
+
+    return NULL;
+}
+
+/* receive RREQs, RREPs and RERRs and handle them */
+static void *_aodv_receiver_thread(void *arg)
+{
+    (void) arg;
+
+    AODV_DEBUG("%s()\n", __func__);
+    uint32_t fromlen;
+    char buf_rcv[UDP_BUFFER_SIZE];
+    msg_t msg_q[RCV_MSG_Q_SIZE];
+
+    msg_init_queue(msg_q, RCV_MSG_Q_SIZE);
+
+    sockaddr6_t sa_rcv = { .sin6_family = AF_INET6,
+                           .sin6_port = HTONS(MANET_PORT)
+                         };
+
+    int sock_rcv = socket_base_socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+
+    if (-1 == socket_base_bind(sock_rcv, &sa_rcv, sizeof(sa_rcv))) {
+        DEBUG("Error: bind to receive socket failed!\n");
+        socket_base_close(sock_rcv);
+    }
+
+    AODV_DEBUG("ready to receive data\n");
+    while (true) {
+        int32_t rcv_size = socket_base_recvfrom(sock_rcv, (void *)buf_rcv, UDP_BUFFER_SIZE, 0,
+                                        &sa_rcv, &fromlen);
+
+        if (rcv_size < 0) {
+            AODV_DEBUG("ERROR receiving data!\n");
+        }
+
+        AODV_DEBUG("_aodv_receiver_thread() %s:",
+              ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &_v6_addr_local));
+        DEBUG(" UDP packet received from %s\n",
+              ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &sa_rcv.sin6_addr));
+
+        struct netaddr _sender;
+        ipv6_addr_t_to_netaddr(&sa_rcv.sin6_addr, &_sender);
+
+        /* For some reason we sometimes get passed our own packets. drop them. */
+        if (netaddr_cmp(&_sender, &na_local) == 0) {
+            AODV_DEBUG("received our own packet, dropping it.\n");
+            aodv_packet_reader_handle_packet((void *) buf_rcv, rcv_size, &_sender);
+        }
+    }
+
+    socket_base_close(sock_rcv);
+
+    return NULL;
+}
+
+ipv6_addr_t *aodv_get_next_hop(ipv6_addr_t *dest)
+{
+    AODV_DEBUG("aodv_get_next_hop() %s:",
+          ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &_v6_addr_local));
+    DEBUG(" getting next hop for %s\n",
+          ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, dest));
+
+    struct netaddr _tmp_dest;
+    ipv6_addr_t_to_netaddr(dest, &_tmp_dest);
+    timex_t now;
+    struct unreachable_node unreachable_nodes[AODVV2_MAX_UNREACHABLE_NODES];
+    size_t len;
+
+    /* The network stack sometimes asks us for the next hop towards our own IP */
+    if (memcmp(dest, &_v6_addr_local, sizeof(ipv6_addr_t)) == 0) {
+        AODV_DEBUG("That's me, returning loopback\n");
+        return &_v6_addr_loopback;
+    }
+
+    /*
+     * TODO use ndp_neighbor_get_ll_address() as soon as it's available.
+     * note: delete check for active/stale/delayed entries, get_ll_address
+     * does that for us then
+    */
+    ndp_neighbor_cache_t *ndp_nc_entry = ndp_neighbor_cache_search(dest);
+    struct aodvv2_routing_entry_t *rt_entry = routingtable_get_entry(&_tmp_dest, _metric_type);
+
+    if (ndp_nc_entry != NULL) {
+        /* Case 2: Broken Link (detected by lower layer) */
+        int link_broken = (ndp_nc_entry->state == NDP_NCE_STATUS_INCOMPLETE ||
+                           ndp_nc_entry->state == NDP_NCE_STATUS_PROBE) &&
+                          (rt_entry != NULL && rt_entry->state != ROUTE_STATE_BROKEN);
+
+        if (link_broken) {
+            DEBUG("\tNeighbor Cache entry found, but invalid (state: %i). Sending RERR.\n",
+                  ndp_nc_entry->state);
+
+            /* mark all routes (active, idle, expired) that use next_hop as broken
+             * and add all *Active* routes to the list of unreachable nodes */
+            routingtable_break_and_get_all_hopping_over(&_tmp_dest, unreachable_nodes, &len);
+
+            aodv_send_rerr(unreachable_nodes, len, &na_mcast);
+            return NULL;
+        }
+
+        DEBUG("[aodvv2][ndp] found NC entry. Returning dest addr.\n");
+        return dest;
+    }
+    DEBUG("\t[ndp] no entry for addr %s found\n",
+          ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, dest));
+
+    if (rt_entry) {
+        /* Case 1: Undeliverable Packet */
+        int packet_indeliverable = rt_entry->state == ROUTE_STATE_BROKEN ||
+                                   rt_entry->state == ROUTE_STATE_EXPIRED;
+        if (packet_indeliverable) {
+            DEBUG("\tRouting table entry found, but invalid (state %i). Sending RERR.\n",
+                  rt_entry->state);
+            unreachable_nodes[0].addr = _tmp_dest;
+            unreachable_nodes[0].seqnum = rt_entry->seqnum;
+            aodv_send_rerr(unreachable_nodes, 1, &na_mcast);
+            return NULL;
+        }
+
+        DEBUG("\tfound dest %s in routing table\n",
+              ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, dest));
+
+        vtimer_now(&now);
+        rt_entry->lastUsed = now;
+        if (rt_entry->state == ROUTE_STATE_IDLE) {
+            rt_entry->state = ROUTE_STATE_ACTIVE;
+        }
+
+        /* Currently, there is no way to do this, so I'm doing it the worst
+         * possible, but safe way: I can't make sure that the current call to
+         * aodv_get_next_hop() is overridden by another call to aodv_get_next_hop()
+         * by a thread with higher priority, thus messing up return values if I just
+         * use a static ipv6_addr_t.
+         * The following malloc will never be free()'d. TODO: FIX THIS ASAP.
+        */
+        ipv6_addr_t *next_hop = (ipv6_addr_t *) malloc(sizeof(ipv6_addr_t));
+        netaddr_to_ipv6_addr_t(&rt_entry->nextHopAddr, next_hop);
+
+        return next_hop;
+    }
+
+    aodvv2_seqnum_t seqnum = seqnum_get();
+    seqnum_inc();
+
+    struct aodvv2_packet_data rreq_data = (struct aodvv2_packet_data) {
+        .hoplimit = AODVV2_MAX_HOPCOUNT,
+        .metricType = _metric_type,
+        .origNode = (struct node_data) {
+            .addr = na_local,
+            .metric = 0,
+            .seqnum = seqnum,
+        },
+        .targNode = (struct node_data) {
+            .addr = _tmp_dest,
+        },
+        .timestamp = (timex_t) {0,0} /* this timestamp is never used, it exists
+                                      * merely to make the compiler shut up */
+    };
+
+    DEBUG("\tNo route found towards %s, starting route discovery... \n",
+          ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, dest));
+    aodv_send_rreq(&rreq_data);
+
+    return NULL;
+}
+
+/**
+ * Handle the output of the RFC5444 packet creation process. This callback is
+ * called by every writer_send_* function.
+ */
+static void _write_packet(struct rfc5444_writer *wr __attribute__ ((unused)),
+                          struct rfc5444_writer_target *iface __attribute__((unused)),
+                          void *buffer, size_t length)
+{
+    AODV_DEBUG("%s()\n", __func__);
+    /* generate hexdump and human readable representation of packet
+     * and print to console */
+    abuf_hexdump(&_hexbuf, "\t", buffer, length);
+    rfc5444_print_direct(&_hexbuf, buffer, length);
+    DEBUG("%s", abuf_getptr(&_hexbuf));
+    abuf_clear(&_hexbuf);
+
+    /* fetch the address the packet is supposed to be sent to (i.e. to a
+     * specific node or the multicast address) from the writer_target struct
+     * iface* is stored in. This is a bit hacky, but it does the trick. */
+    wt = container_of(iface, struct writer_target, interface);
+    netaddr_to_ipv6_addr_t(&wt->target_addr, &sa_wp.sin6_addr);
+
+    /* When originating a RREQ, add it to our RREQ table/update its predecessor */
+    if (wt->type == RFC5444_MSGTYPE_RREQ
+        && netaddr_cmp(&wt->packet_data.origNode.addr, &na_local) == 0) {
+        AODV_DEBUG("originating RREQ with SeqNum %d towards %s via %s; updating RREQ table...\n",
+              wt->packet_data.origNode.seqnum,
+              netaddr_to_string(&nbuf, &wt->packet_data.targNode.addr),
+              ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &sa_wp.sin6_addr));
+        rreqtable_is_redundant(&wt->packet_data);
+    }
+
+    int bytes_sent = socket_base_sendto(_sock_snd, buffer, length,
+                                        0, &sa_wp, sizeof sa_wp);
+
+    (void) bytes_sent;
+    AODV_DEBUG("%d bytes sent.\n", bytes_sent);
+}
+
+/* free the matryoshka doll of cobbled-together structs that the sender_thread receives */
+static void _deep_free_msg_container(struct msg_container *mc)
+{
+    int type = mc->type;
+    if ((type == RFC5444_MSGTYPE_RREQ) || (type == RFC5444_MSGTYPE_RREP)) {
+        struct rreq_rrep_data *rreq_rrep_data = (struct rreq_rrep_data *) mc->data;
+        free(rreq_rrep_data->packet_data);
+        if (netaddr_cmp(rreq_rrep_data->next_hop, &na_mcast) != 0) {
+            free(rreq_rrep_data->next_hop);
+        }
+    }
+    else if (type == RFC5444_MSGTYPE_RERR) {
+        struct rerr_data *rerr_data = (struct rerr_data *) mc->data;
+        if (netaddr_cmp(rerr_data->next_hop, &na_mcast) != 0) {
+            free(rerr_data->next_hop);
+        }
+    }
+    free(mc->data);
+    free(mc);
+}
diff --git a/sys/net/routing/aodvv2/aodv.h b/sys/net/routing/aodvv2/aodv.h
new file mode 100644
index 0000000000..f210b7e22e
--- /dev/null
+++ b/sys/net/routing/aodvv2/aodv.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        aodv.h
+ * @brief       aodvv2 routing protocol
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#ifndef AODV_H_
+#define AODV_H_
+
+#include <sixlowpan/ip.h>
+#include "sixlowpan.h"
+#include "kernel.h"
+#include "udp.h"
+#include "socket_base/socket.h"
+#include "net_help.h"
+#include "net_if.h"
+
+#include "aodvv2/types.h"
+#include "constants.h"
+#include "seqnum.h"
+#include "routingtable.h"
+#include "utils.h"
+#include "reader.h"
+#include "writer.h"
+#include "thread.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   This struct contains data which needs to be put into a RREQ or RREP.
+ *          It is used to transport this data in a message to the sender_thread.
+ * @note    Please note that it is for internal use only. To send a RREQ or RREP,
+ *          please use the aodv_send_rreq() and aodv_send_rrep() functions.
+ */
+struct rreq_rrep_data
+{
+    struct aodvv2_packet_data *packet_data;     /**< Data for the RREQ or RREP */
+    struct netaddr *next_hop;                   /**< Next hop to which the RREQ
+                                                 *   or RREP should be sent */
+};
+
+/**
+ * @brief   This struct contains data which needs to be put into a RERR.
+ *          It is used to transport this data in a message to the sender_thread.
+ * @note    Please note that it is for internal use only. To send a RERR,
+ *          please use the aodv_send_rerr() function.
+ */
+struct rerr_data
+{
+    struct unreachable_node *unreachable_nodes; /**< All unreachable nodes. Beware,
+                                                 *   this is the start of an array */
+    size_t len;                                 /**< Length of the unreachable_nodes array */
+    int hoplimit;                               /**< hoplimit for the RERR */
+    struct netaddr *next_hop;                   /**< Next hop to which the RERR
+                                                 *   should be sent */
+};
+
+
+/**
+ * @brief   This struct holds the data for a RREQ, RREP or RERR (contained
+ *          in a rreq_rrep_data or rerr_data struct) and the next hop the RREQ, RREP
+ *          or RERR should be sent to. It used for message communication with
+ *          the sender_thread.
+ * @note    Please note that it is for internal use only. To send a RERR,
+ *          please use the aodv_send_rerr() function.
+ */
+struct msg_container
+{
+    int type;                                   /**< Message type (i.e. one of
+                                                 *   rfc5444_msg_type) */
+    void *data;                                 /**< Pointer to the message data
+                                                 * (i.e. rreq_rrep_data or rerr_data) */
+};
+
+/**
+ * @brief   When set as ipv6_iface_routing_provider, this function is called by
+ *          ipv6_sendto() to determine the next hop towards dest. This function
+ *          is non-blocking.
+ *
+ * @param[in] dest  destination of the packet
+ * @return          Address of the next hop towards dest if there is any,
+ *                  NULL if there is none (yet)
+ */
+ipv6_addr_t *aodv_get_next_hop(ipv6_addr_t *dest);
+
+/**
+ * @brief   Dispatch a RREQ
+ *
+ * @param[in] packet_data  Payload of the RREQ
+ */
+void aodv_send_rreq(struct aodvv2_packet_data *packet_data);
+
+/**
+ * @brief   Dispatch a RREP
+ *
+ * @param[in] packet_data  Payload of the RREP
+ * @param[in] next_hop     Address of the next hop the RREP should be sent to
+ */
+void aodv_send_rrep(struct aodvv2_packet_data *packet_data, struct netaddr *next_hop);
+
+/**
+ * @brief   Dispatch a RERR
+ *
+ * @param[in] unreachable_nodes  All nodes that are marked as unreachable
+ *                               by this RERR
+ * @param[in] len                Number of unreachable nodes
+ * @param[in] next_hop           Address of the next hop the RERR should be sent to
+ */
+void aodv_send_rerr(struct unreachable_node unreachable_nodes[], size_t len,
+                    struct netaddr *next_hop);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* AODV_H_ */
diff --git a/sys/net/routing/aodvv2/aodv_debug.h b/sys/net/routing/aodvv2/aodv_debug.h
new file mode 100644
index 0000000000..75d681f369
--- /dev/null
+++ b/sys/net/routing/aodvv2/aodv_debug.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup  core_util
+ * @{
+ *
+ * @ingroup     aodvv2
+ * @brief       Debug-header for aodvv2 debug messages
+ *
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#ifndef AODV_DEBUG_H_
+#define AODV_DEBUG_H_
+
+#include <stdio.h>
+#include "sched.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#ifdef DEBUG_ENABLED
+#define ENABLE_AODV_DEBUG (1)
+#endif
+
+/**
+ * @brief Print aodvv2 specific debug information to std-out with [aodvv2] prefix
+ *
+ */
+#if ENABLE_AODV_DEBUG
+#include "tcb.h"
+#define AODV_DEBUG(...) \
+ do { \
+        printf("[aodvv2] "); \
+        printf(__VA_ARGS__); \
+    } while (0)
+#else
+#define AODV_DEBUG(...)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AODVV2_DEBUG_H_*/
+/** @} */
diff --git a/sys/net/routing/aodvv2/constants.h b/sys/net/routing/aodvv2/constants.h
new file mode 100644
index 0000000000..3567ccfce9
--- /dev/null
+++ b/sys/net/routing/aodvv2/constants.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        constants.h
+ * @brief       constants for the aodvv2 routing protocol
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#ifndef AODVV2_CONSTANTS_H_
+#define AODVV2_CONSTANTS_H_
+
+#include "aodvv2/types.h"
+
+#include "common/netaddr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MANET_PORT  269                 /** RFC5498 */
+
+enum aodvv2_constants {
+    AODVV2_MAX_HOPCOUNT = 250,          /**< see AODVv2 draft, section 14.2.*/
+    AODVV2_MAX_ROUTING_ENTRIES = 255,   /**< maximum number of entries
+                                         *   in the routing table */
+    AODVV2_ACTIVE_INTERVAL = 5,         /**< seconds */
+    AODVV2_MAX_IDLETIME = 250,          /**< seconds */
+    AODVV2_MAX_SEQNUM_LIFETIME = 300,   /**< seconds */
+    AODVV2_MAX_UNREACHABLE_NODES = 15,  /**< TODO: choose value (wisely) */
+};
+
+/**
+ * @brief   TLV type array indices
+ */
+enum tlv_index
+{
+    TLV_ORIGSEQNUM,
+    TLV_TARGSEQNUM,
+    TLV_UNREACHABLE_NODE_SEQNUM,
+    TLV_METRIC,
+};
+
+/* my multicast address */
+extern struct netaddr na_mcast;
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* AODVV2_CONSTANTS_H_ */
diff --git a/sys/net/routing/aodvv2/reader.c b/sys/net/routing/aodvv2/reader.c
new file mode 100644
index 0000000000..1333a807fb
--- /dev/null
+++ b/sys/net/routing/aodvv2/reader.c
@@ -0,0 +1,754 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        reader.c
+ * @brief       reading and handling of RFC5444 aodvv2 messages
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#ifdef RIOT
+#include "net_help.h"
+#endif
+
+#include "debug.h"
+
+#include "reader.h"
+#include "aodv_debug.h"
+
+#define ENABLE_DEBUG (0)
+
+#define VERBOSE_DEBUG (0)
+#if VERBOSE_DEBUG
+#define VDEBUG(...) AODV_DEBUG(__VA_ARGS__)
+#else
+#define VDEBUG(...)
+#endif
+
+static enum rfc5444_result _cb_rreq_blocktlv_addresstlvs_okay(
+    struct rfc5444_reader_tlvblock_context *cont);
+static enum rfc5444_result _cb_rreq_blocktlv_messagetlvs_okay(
+    struct rfc5444_reader_tlvblock_context *cont);
+static enum rfc5444_result _cb_rreq_end_callback(
+    struct rfc5444_reader_tlvblock_context *cont, bool dropped);
+
+static enum rfc5444_result _cb_rrep_blocktlv_addresstlvs_okay(
+    struct rfc5444_reader_tlvblock_context *cont);
+static enum rfc5444_result _cb_rrep_blocktlv_messagetlvs_okay(
+    struct rfc5444_reader_tlvblock_context *cont);
+static enum rfc5444_result _cb_rrep_end_callback(
+    struct rfc5444_reader_tlvblock_context *cont, bool dropped);
+
+static enum rfc5444_result _cb_rerr_blocktlv_addresstlvs_okay(
+    struct rfc5444_reader_tlvblock_context *cont);
+static enum rfc5444_result _cb_rerr_blocktlv_messagetlvs_okay(
+    struct rfc5444_reader_tlvblock_context *cont);
+static enum rfc5444_result _cb_rerr_end_callback(
+    struct rfc5444_reader_tlvblock_context *cont, bool dropped);
+
+/* helper functions */
+static uint8_t _get_link_cost(aodvv2_metric_t metricType);
+static uint8_t _get_max_metric(aodvv2_metric_t metricType);
+static void _update_metric(aodvv2_metric_t metricType, uint8_t *metric);
+
+/* This is where we store data gathered from packets */
+static struct aodvv2_packet_data packet_data;
+static struct unreachable_node unreachable_nodes[AODVV2_MAX_UNREACHABLE_NODES];
+static int num_unreachable_nodes;
+
+static struct rfc5444_reader reader;
+#ifdef DEBUG_ENABLED
+static struct netaddr_str nbuf;
+#endif
+
+/*
+ * Message consumer, will be called once for every message of
+ * type RFC5444_MSGTYPE_RREQ that contains all the mandatory message TLVs
+ */
+static struct rfc5444_reader_tlvblock_consumer _rreq_consumer =
+{
+    .msg_id = RFC5444_MSGTYPE_RREQ,
+    .block_callback = _cb_rreq_blocktlv_messagetlvs_okay,
+    .end_callback = _cb_rreq_end_callback,
+};
+
+/*
+ * Address consumer. Will be called once for every address in a message of
+ * type RFC5444_MSGTYPE_RREQ.
+ */
+static struct rfc5444_reader_tlvblock_consumer _rreq_address_consumer =
+{
+    .msg_id = RFC5444_MSGTYPE_RREQ,
+    .addrblock_consumer = true,
+    .block_callback = _cb_rreq_blocktlv_addresstlvs_okay,
+};
+
+/*
+ * Message consumer, will be called once for every message of
+ * type RFC5444_MSGTYPE_RREP that contains all the mandatory message TLVs
+ */
+static struct rfc5444_reader_tlvblock_consumer _rrep_consumer =
+{
+    .msg_id = RFC5444_MSGTYPE_RREP,
+    .block_callback = _cb_rrep_blocktlv_messagetlvs_okay,
+    .end_callback = _cb_rrep_end_callback,
+};
+
+/*
+ * Address consumer. Will be called once for every address in a message of
+ * type RFC5444_MSGTYPE_RREP.
+ */
+static struct rfc5444_reader_tlvblock_consumer _rrep_address_consumer =
+{
+    .msg_id = RFC5444_MSGTYPE_RREP,
+    .addrblock_consumer = true,
+    .block_callback = _cb_rrep_blocktlv_addresstlvs_okay,
+};
+
+/*
+ * Message consumer, will be called once for every message of
+ * type RFC5444_MSGTYPE_RERR that contains all the mandatory message TLVs
+ */
+static struct rfc5444_reader_tlvblock_consumer _rerr_consumer =
+{
+    .msg_id = RFC5444_MSGTYPE_RERR,
+    .block_callback = _cb_rerr_blocktlv_messagetlvs_okay,
+    .end_callback = _cb_rerr_end_callback,
+};
+
+/*
+ * Address consumer. Will be called once for every address in a message of
+ * type RFC5444_MSGTYPE_RERR.
+ */
+static struct rfc5444_reader_tlvblock_consumer _rerr_address_consumer =
+{
+    .msg_id = RFC5444_MSGTYPE_RERR,
+    .addrblock_consumer = true,
+    .block_callback = _cb_rerr_blocktlv_addresstlvs_okay,
+};
+
+
+/*
+ * Address consumer entries definition
+ * TLV types RFC5444_MSGTLV__SEQNUM and RFC5444_MSGTLV_METRIC
+ */
+static struct rfc5444_reader_tlvblock_consumer_entry _rreq_rrep_address_consumer_entries[] =
+{
+    [RFC5444_MSGTLV_ORIGSEQNUM] = { .type = RFC5444_MSGTLV_ORIGSEQNUM},
+    [RFC5444_MSGTLV_TARGSEQNUM] = { .type = RFC5444_MSGTLV_TARGSEQNUM},
+    [RFC5444_MSGTLV_METRIC] = { .type = RFC5444_MSGTLV_METRIC }
+};
+
+/*
+ * Address consumer entries definition
+ * TLV types RFC5444_MSGTLV__SEQNUM and RFC5444_MSGTLV_METRIC
+ */
+static struct rfc5444_reader_tlvblock_consumer_entry _rerr_address_consumer_entries[] =
+{
+    [RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM] = {
+        .type = RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM
+    },
+};
+
+/**
+ * This block callback is called for every address
+ *
+ * @param cont
+ * @return
+ */
+static enum rfc5444_result _cb_rreq_blocktlv_messagetlvs_okay(struct rfc5444_reader_tlvblock_context *cont)
+{
+    VDEBUG("%s()\n", __func__);
+
+    if (!cont->has_hoplimit) {
+        DEBUG("\tERROR: missing hop limit\n");
+        return RFC5444_DROP_PACKET;
+    }
+
+    packet_data.hoplimit = cont->hoplimit;
+    if (packet_data.hoplimit == 0) {
+        DEBUG("\tERROR: Hoplimit is 0.\n");
+        return RFC5444_DROP_PACKET;
+    }
+    packet_data.hoplimit--;
+    return RFC5444_OKAY;
+}
+
+/**
+ * This block callback is called for every address of a RREQ Message.
+ *
+ * @param cont
+ * @return
+ */
+static enum rfc5444_result _cb_rreq_blocktlv_addresstlvs_okay(struct rfc5444_reader_tlvblock_context *cont)
+{
+#ifdef DEBUG_ENABLED
+    struct netaddr_str nbuf;
+#endif
+    struct rfc5444_reader_tlvblock_entry *tlv;
+    bool is_origNode_addr = false;
+    bool is_targNode_addr = false;
+
+    VDEBUG("%s()\n", __func__);
+    DEBUG("\taddr: %s\n", netaddr_to_string(&nbuf, &cont->addr));
+
+    /* handle OrigNode SeqNum TLV */
+    tlv = _rreq_rrep_address_consumer_entries[RFC5444_MSGTLV_ORIGSEQNUM].tlv;
+    if (tlv) {
+        DEBUG("\ttlv RFC5444_MSGTLV_ORIGSEQNUM: %d\n", *tlv->single_value);
+        is_origNode_addr = true;
+        packet_data.origNode.addr = cont->addr;
+        packet_data.origNode.seqnum = *tlv->single_value;
+    }
+
+    /* handle TargNode SeqNum TLV */
+    tlv = _rreq_rrep_address_consumer_entries[RFC5444_MSGTLV_TARGSEQNUM].tlv;
+    if (tlv) {
+        DEBUG("\ttlv RFC5444_MSGTLV_TARGSEQNUM: %d\n", *tlv->single_value);
+        is_targNode_addr = true;
+        packet_data.targNode.addr = cont->addr;
+        packet_data.targNode.seqnum = *tlv->single_value;
+    }
+    if (!tlv && !is_origNode_addr) {
+        /* assume that tlv missing => targNode Address */
+        is_targNode_addr = true;
+        packet_data.targNode.addr = cont->addr;
+    }
+    if (!is_origNode_addr && !is_targNode_addr) {
+        DEBUG("\tERROR: mandatory RFC5444_MSGTLV_ORIGSEQNUM TLV missing.\n");
+        return RFC5444_DROP_PACKET;
+    }
+
+    /* handle Metric TLV */
+    /* cppcheck: suppress false positive on non-trivially initialized arrays.
+     *           this is a known bug: http://trac.cppcheck.net/ticket/5497 */
+    /* cppcheck-suppress arrayIndexOutOfBounds */
+    tlv = _rreq_rrep_address_consumer_entries[RFC5444_MSGTLV_METRIC].tlv;
+    if (!tlv && is_origNode_addr) {
+        DEBUG("\tERROR: Missing or unknown metric TLV.\n");
+        return RFC5444_DROP_PACKET;
+    }
+    if (tlv) {
+        if (!is_origNode_addr) {
+            DEBUG("\tERROR: Metric TLV belongs to wrong address.\n");
+            return RFC5444_DROP_PACKET;
+        }
+        VDEBUG("\ttlv RFC5444_MSGTLV_METRIC val: %d, exttype: %d\n",
+               *tlv->single_value, tlv->type_ext);
+        packet_data.metricType = tlv->type_ext;
+        packet_data.origNode.metric = *tlv->single_value;
+    }
+    return RFC5444_OKAY;
+}
+
+/**
+ * This callback is called every time the _rreq_consumer finishes reading a
+ * packet.
+ * @param cont
+ * @param dropped indicates whether the packet has been dropped previously by
+ *                another callback
+ */
+static enum rfc5444_result _cb_rreq_end_callback(
+    struct rfc5444_reader_tlvblock_context *cont, bool dropped)
+{
+    (void) cont;
+
+    struct aodvv2_routing_entry_t *rt_entry;
+    timex_t now;
+    uint8_t link_cost = _get_link_cost(packet_data.metricType);
+
+    /* Check if packet contains the required information */
+    if (dropped) {
+        DEBUG("\t Dropping packet.\n");
+        return RFC5444_DROP_PACKET;
+    }
+    if ((packet_data.origNode.addr._type == AF_UNSPEC) || !packet_data.origNode.seqnum) {
+        DEBUG("\tERROR: missing OrigNode Address or SeqNum. Dropping packet.\n");
+        return RFC5444_DROP_PACKET;
+    }
+    if (packet_data.targNode.addr._type == AF_UNSPEC) {
+        DEBUG("\tERROR: missing TargNode Address. Dropping packet.\n");
+        return RFC5444_DROP_PACKET;
+    }
+    if (packet_data.hoplimit == 0) {
+        DEBUG("\tERROR: Hoplimit is 0. Dropping packet.\n");
+        return RFC5444_DROP_PACKET;
+    }
+    if ((_get_max_metric(packet_data.metricType) - link_cost)
+        <= packet_data.origNode.metric) {
+        DEBUG("\tMetric Limit reached. Dropping packet.\n");
+        return RFC5444_DROP_PACKET;
+    }
+
+    /*
+      The incoming RREQ MUST be checked against previously received
+      information from the RREQ Table Section 7.6.  If the information
+      in the incoming RteMsg is redundant, then then no further action
+      is taken.
+    */
+    if (rreqtable_is_redundant(&packet_data)) {
+        DEBUG("\tPacket is redundant. Dropping Packet. %i\n", RFC5444_DROP_PACKET);
+        return RFC5444_DROP_PACKET;
+    }
+
+    _update_metric(packet_data.metricType, &packet_data.origNode.metric);
+    vtimer_now(&now);
+    packet_data.timestamp = now;
+
+    /* for every relevant
+     * address (RteMsg.Addr) in the RteMsg, HandlingRtr searches its route
+     * table to see if there is a route table entry with the same MetricType
+     * of the RteMsg, matching RteMsg.Addr.
+     */
+
+    rt_entry = routingtable_get_entry(&packet_data.origNode.addr, packet_data.metricType);
+
+    if (!rt_entry || (rt_entry->metricType != packet_data.metricType)) {
+        /* CAUTION SUPER HACKY FIX FIXME ASAP
+        problem: sometimes we get broadcasted RREQs from 2 hop neighbors and then
+        AODVv2 gets super confused when they're not in the routing table and starts a
+        Route discovery to find them and all hell breaks loose. let's see if we can fix
+        this (horribly).
+
+        (another fix would be to stop bouncing the RREP back to the sender and asking
+        the routing table for the next hop (or just send towards TargNode and let the
+        network stack figure out the rest?))
+        TODO evaluate that
+        */
+
+        ipv6_addr_t sender_tmp;
+        netaddr_to_ipv6_addr_t(&packet_data.sender, &sender_tmp);
+        ndp_neighbor_cache_t *ndp_nc_entry = ndp_neighbor_cache_search(&sender_tmp);
+
+        if (ndp_nc_entry == NULL) {
+            DEBUG("OH NOES! No bidirectional link to sender. Dropping packet.\n");
+            return RFC5444_DROP_PACKET;
+        }
+        /* HACKY FIX ENDS HERE */
+
+        VDEBUG("\tCreating new Routing Table entry...\n");
+
+        struct aodvv2_routing_entry_t *tmp_rt_entry = (struct aodvv2_routing_entry_t *)
+                                                       malloc(sizeof(struct aodvv2_routing_entry_t));
+        memset(tmp_rt_entry, 0, sizeof(*tmp_rt_entry));
+
+        routingtable_fill_routing_entry_t_rreq(&packet_data, tmp_rt_entry, link_cost);
+        routingtable_add_entry(tmp_rt_entry);
+
+        free(tmp_rt_entry);
+    }
+    else {
+        if (!routingtable_offers_improvement(rt_entry, &packet_data.origNode)) {
+            DEBUG("\tPacket offers no improvement over known route. Dropping Packet.\n");
+            return RFC5444_DROP_PACKET;
+        }
+        /* The incoming routing information is better than existing routing
+         * table information and SHOULD be used to improve the route table. */
+        VDEBUG("\tUpdating Routing Table entry...\n");
+        routingtable_fill_routing_entry_t_rreq(&packet_data, rt_entry, link_cost);
+    }
+
+    /*
+     * If TargNode is a client of the router receiving the RREQ, then the
+     * router generates a RREP message as specified in Section 7.4, and
+     * subsequently processing for the RREQ is complete.  Otherwise,
+     * processing continues as follows.
+     */
+    if (clienttable_is_client(&packet_data.targNode.addr)) {
+        AODV_DEBUG("TargNode is in client list, sending RREP\n");
+
+        /* make sure to start with a clean metric value */
+        packet_data.targNode.metric = 0;
+        aodv_send_rrep(&packet_data, &packet_data.sender);
+    }
+    else {
+        AODV_DEBUG("I am not TargNode, forwarding RREQ\n");
+        aodv_send_rreq(&packet_data);
+    }
+    return RFC5444_OKAY;
+}
+
+/**
+ * This block callback is called for every address
+ *
+ * @param cont
+ * @return
+ */
+static enum rfc5444_result _cb_rrep_blocktlv_messagetlvs_okay(struct rfc5444_reader_tlvblock_context *cont)
+{
+    VDEBUG("%s()\n", __func__);
+
+    if (!cont->has_hoplimit) {
+        VDEBUG("\tERROR: missing hop limit\n");
+        return RFC5444_DROP_PACKET;
+    }
+
+    packet_data.hoplimit = cont->hoplimit;
+    if (packet_data.hoplimit == 0) {
+        VDEBUG("\tERROR: Hoplimit is 0.\n");
+        return RFC5444_DROP_PACKET;
+    }
+
+    packet_data.hoplimit--;
+    return RFC5444_OKAY;
+}
+
+/**
+ * This block callback is called for every address of a RREP Message.
+ *
+ * @param cont
+ * @return
+ */
+static enum rfc5444_result _cb_rrep_blocktlv_addresstlvs_okay(struct rfc5444_reader_tlvblock_context *cont)
+{
+#ifdef DEBUG_ENABLED
+    struct netaddr_str nbuf;
+#endif
+    struct rfc5444_reader_tlvblock_entry *tlv;
+    bool is_targNode_addr = false;
+
+    VDEBUG("%s()\n", __func__);
+    VDEBUG("\taddr: %s\n", netaddr_to_string(&nbuf, &cont->addr));
+
+    /* handle TargNode SeqNum TLV */
+    tlv = _rreq_rrep_address_consumer_entries[RFC5444_MSGTLV_TARGSEQNUM].tlv;
+    if (tlv) {
+        VDEBUG("\ttlv RFC5444_MSGTLV_TARGSEQNUM: %d\n", *tlv->single_value);
+        is_targNode_addr = true;
+        packet_data.targNode.addr = cont->addr;
+        packet_data.targNode.seqnum = *tlv->single_value;
+    }
+
+    /* handle OrigNode SeqNum TLV */
+    tlv = _rreq_rrep_address_consumer_entries[RFC5444_MSGTLV_ORIGSEQNUM].tlv;
+    if (tlv) {
+        VDEBUG("\ttlv RFC5444_MSGTLV_ORIGSEQNUM: %d\n", *tlv->single_value);
+        is_targNode_addr = false;
+        packet_data.origNode.addr = cont->addr;
+        packet_data.origNode.seqnum = *tlv->single_value;
+    }
+    if (!tlv && !is_targNode_addr) {
+        DEBUG("\tERROR: mandatory SeqNum TLV missing.\n");
+        return RFC5444_DROP_PACKET;
+    }
+
+    /* handle Metric TLV */
+    /* cppcheck: suppress false positive on non-trivially initialized arrays.
+     *           this is a known bug: http://trac.cppcheck.net/ticket/5497 */
+    /* cppcheck-suppress arrayIndexOutOfBounds */
+    tlv = _rreq_rrep_address_consumer_entries[RFC5444_MSGTLV_METRIC].tlv;
+    if (!tlv && is_targNode_addr) {
+        DEBUG("\tERROR: Missing or unknown metric TLV.\n");
+        return RFC5444_DROP_PACKET;
+    }
+    if (tlv) {
+        if (!is_targNode_addr) {
+            DEBUG("\tERROR: metric TLV belongs to wrong address.\n");
+            return RFC5444_DROP_PACKET;
+        }
+        VDEBUG("\ttlv RFC5444_MSGTLV_METRIC val: %d, exttype: %d\n",
+               *tlv->single_value, tlv->type_ext);
+        packet_data.metricType = tlv->type_ext;
+        packet_data.origNode.metric = *tlv->single_value;
+    }
+    return RFC5444_OKAY;
+}
+
+/**
+ * This callback is called every time the _rreq_consumer finishes reading a
+ * packet.
+ * @param cont
+ * @param dropped indicates wehther the packet has been dropped previously by
+ *                another callback
+ */
+static enum rfc5444_result _cb_rrep_end_callback(
+    struct rfc5444_reader_tlvblock_context *cont, bool dropped)
+{
+    (void) cont;
+
+    VDEBUG("%s()\n", __func__);
+
+    struct aodvv2_routing_entry_t *rt_entry;
+#ifdef DEBUG_ENABLED
+    struct netaddr_str nbuf;
+#endif
+    timex_t now;
+    uint8_t link_cost = _get_link_cost(packet_data.metricType);
+
+    /* Check if packet contains the required information */
+    if (dropped) {
+        DEBUG("\t Dropping packet.\n");
+        return RFC5444_DROP_PACKET;
+    }
+    if ((packet_data.origNode.addr._type == AF_UNSPEC)
+        || !packet_data.origNode.seqnum) {
+        DEBUG("\tERROR: missing OrigNode Address or SeqNum. Dropping packet.\n");
+        return RFC5444_DROP_PACKET;
+    }
+    if ((packet_data.targNode.addr._type == AF_UNSPEC)
+        || !packet_data.targNode.seqnum) {
+        DEBUG("\tERROR: missing TargNode Address or SeqNum. Dropping packet.\n");
+        return RFC5444_DROP_PACKET;
+    }
+    if ((_get_max_metric(packet_data.metricType) - link_cost)
+        <= packet_data.targNode.metric) {
+        DEBUG("\tMetric Limit reached. Dropping packet.\n");
+        return RFC5444_DROP_PACKET;
+    }
+
+    _update_metric(packet_data.metricType, &packet_data.targNode.metric);
+    vtimer_now(&now);
+    packet_data.timestamp = now;
+
+    /* for every relevant address (RteMsg.Addr) in the RteMsg, HandlingRtr
+    searches its route table to see if there is a route table entry with the
+    same MetricType of the RteMsg, matching RteMsg.Addr. */
+
+    rt_entry = routingtable_get_entry(&packet_data.targNode.addr, packet_data.metricType);
+
+    if (!rt_entry || (rt_entry->metricType != packet_data.metricType)) {
+        /* CAUTION SUPER HACKY FIX FIXME ASAP
+        problem: sometimes we get broadcasted RREQs from 2 hop neighbors and then
+        AODVv2 gets super confused when they're not in the routing table and starts a
+        Route discovery to find them and all hell breaks loose. let's see if we can fix
+        this (horribly).
+
+        (another fix would be to stop bouncing the RREP back to the sender and asking
+        the routing table for the next hop (or just send towards TargNode and let the network stack figure out the rest?))
+        TODO evaluate that
+        */
+
+        ipv6_addr_t sender_tmp;
+        netaddr_to_ipv6_addr_t(&packet_data.sender, &sender_tmp);
+        ndp_neighbor_cache_t *ndp_nc_entry = ndp_neighbor_cache_search(&sender_tmp);
+
+        if (ndp_nc_entry == NULL) {
+            DEBUG("OH NOES! No bidirectional link to sender. Dropping packet.\n");
+            return RFC5444_DROP_PACKET;
+        }
+        /* HACKY FIX ENDS HERE */
+        VDEBUG("\tCreating new Routing Table entry...\n");
+
+        struct aodvv2_routing_entry_t *tmp_rt_entry = (struct aodvv2_routing_entry_t *)
+                                                       malloc(sizeof(struct aodvv2_routing_entry_t));
+        memset(tmp_rt_entry, 0, sizeof(*tmp_rt_entry));
+
+        routingtable_fill_routing_entry_t_rrep(&packet_data, tmp_rt_entry, link_cost);
+        routingtable_add_entry(tmp_rt_entry);
+
+        free(tmp_rt_entry);
+    }
+    else {
+        if (!routingtable_offers_improvement(rt_entry, &packet_data.targNode)) {
+            DEBUG("\tPacket offers no improvement over known route. Dropping Packet.\n");
+            return RFC5444_DROP_PACKET;
+        }
+        /* The incoming routing information is better than existing routing
+         * table information and SHOULD be used to improve the route table. */
+        VDEBUG("\tUpdating Routing Table entry...\n");
+        routingtable_fill_routing_entry_t_rrep(&packet_data, rt_entry, link_cost);
+    }
+
+    /* If HandlingRtr is RREQ_Gen then the RREP satisfies RREQ_Gen's
+    earlier RREQ, and RREP processing is completed.  Any packets
+    buffered for OrigNode should be transmitted. */
+    if (clienttable_is_client(&packet_data.origNode.addr)) {
+#ifdef DEBUG_ENABLED
+        static struct netaddr_str nbuf2;
+#endif
+
+        DEBUG("\t{%" PRIu32 ":%" PRIu32 "} %s:  This is my RREP (SeqNum: %d). We are done here, thanks %s!\n",
+              now.seconds, now.microseconds, netaddr_to_string(&nbuf, &packet_data.origNode.addr),
+              packet_data.origNode.seqnum, netaddr_to_string(&nbuf2, &packet_data.targNode.addr));
+    }
+
+    else {
+        /* If HandlingRtr is not RREQ_Gen then the outgoing RREP is sent to the
+         * Route.NextHopAddress for the RREP.AddrBlk[OrigNodeNdx]. */
+        AODV_DEBUG("Not my RREP, passing it on to the next hop\n");
+        aodv_send_rrep(&packet_data,
+                       routingtable_get_next_hop(&packet_data.origNode.addr,packet_data.metricType));
+    }
+    return RFC5444_OKAY;
+}
+
+static enum rfc5444_result _cb_rerr_blocktlv_messagetlvs_okay(struct rfc5444_reader_tlvblock_context *cont)
+{
+    VDEBUG("%s()\n", __func__);
+
+    if (!cont->has_hoplimit) {
+        VDEBUG("\tERROR: missing hop limit\n");
+        return RFC5444_DROP_PACKET;
+    }
+
+    packet_data.hoplimit = cont->hoplimit;
+    if (packet_data.hoplimit == 0) {
+        VDEBUG("\tERROR: Hoplimit is 0.\n");
+        return RFC5444_DROP_PACKET;
+    }
+
+    packet_data.hoplimit--;
+
+    /* prepare buffer for unreachable nodes */
+    num_unreachable_nodes = 0;
+    for (unsigned i = 0; i < AODVV2_MAX_UNREACHABLE_NODES; i++) {
+        memset(&unreachable_nodes[i], 0, sizeof(unreachable_nodes[i]));
+    }
+    return RFC5444_OKAY;
+}
+
+static enum rfc5444_result _cb_rerr_blocktlv_addresstlvs_okay(struct rfc5444_reader_tlvblock_context *cont)
+{
+#ifdef DEBUG_ENABLED
+    struct netaddr_str nbuf;
+#endif
+    struct aodvv2_routing_entry_t *unreachable_entry;
+    struct rfc5444_reader_tlvblock_entry *tlv;
+
+    VDEBUG("%s()\n", __func__);
+    VDEBUG("\tmessage type: %d\n", cont->type);
+    VDEBUG("\taddr: %s\n", netaddr_to_string(&nbuf, &cont->addr));
+
+    /* Out of buffer size for more unreachable nodes. We're screwed, basically. */
+    if (num_unreachable_nodes == AODVV2_MAX_UNREACHABLE_NODES) {
+        return RFC5444_OKAY;
+    }
+
+    /* gather packet data */
+    packet_data.origNode.addr = cont->addr;
+
+    /* handle this unreachable node's SeqNum TLV */
+    /* cppcheck: suppress false positive on non-trivially initialized arrays.
+     *           this is a known bug: http://trac.cppcheck.net/ticket/5497 */
+    /* cppcheck-suppress arrayIndexOutOfBounds */
+    tlv = _rerr_address_consumer_entries[RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM].tlv;
+    if (tlv) {
+        VDEBUG("\ttlv RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM: %d\n", *tlv->single_value);
+        packet_data.origNode.seqnum = *tlv->single_value;
+    }
+
+    /* Check if there is an entry for unreachable node in our routing table */
+    unreachable_entry = routingtable_get_entry(&packet_data.origNode.addr, packet_data.metricType);
+    if (unreachable_entry) {
+        VDEBUG("\t found possibly unreachable entry.\n");
+
+        /* check if route to unreachable node has to be marked as broken and RERR has to be forwarded */
+        if (netaddr_cmp(&unreachable_entry->nextHopAddr, &packet_data.sender) == 0
+                && (!tlv || seqnum_cmp(unreachable_entry->seqnum, packet_data.origNode.seqnum) == 0)) {
+            unreachable_entry->state = ROUTE_STATE_BROKEN;
+            unreachable_nodes[num_unreachable_nodes].addr = packet_data.origNode.addr;
+            unreachable_nodes[num_unreachable_nodes].seqnum = packet_data.origNode.seqnum;
+            num_unreachable_nodes++;
+        }
+    }
+    return RFC5444_OKAY;
+}
+
+static enum rfc5444_result _cb_rerr_end_callback(struct rfc5444_reader_tlvblock_context *cont, bool dropped)
+{
+    (void) cont;
+
+    if (dropped) {
+        VDEBUG("\tDropping packet.\n");
+        return RFC5444_DROP_PACKET;
+    }
+
+    if (num_unreachable_nodes == 0) {
+        VDEBUG("\tNo unreachable nodes from my routing table. Dropping Packet.\n");
+        return RFC5444_DROP_PACKET;
+    }
+    /* gather all unreachable nodes and put them into a RERR */
+    aodv_send_rerr(unreachable_nodes, num_unreachable_nodes, &na_mcast);
+    return RFC5444_OKAY;
+}
+
+void aodv_packet_reader_init(void)
+{
+    VDEBUG("%s()\n", __func__);
+
+    /* initialize reader */
+    rfc5444_reader_init(&reader);
+
+    /* register message consumers. We have no message TLVs, so we can leave the
+     * rfc5444_reader_tlvblock_consumer_entry empty */
+    rfc5444_reader_add_message_consumer(&reader, &_rreq_consumer,
+                                        NULL, 0);
+    rfc5444_reader_add_message_consumer(&reader, &_rrep_consumer,
+                                        NULL, 0);
+    rfc5444_reader_add_message_consumer(&reader, &_rerr_consumer,
+                                        NULL, 0);
+
+    /* register address consumer */
+    rfc5444_reader_add_message_consumer(&reader, &_rreq_address_consumer,
+                                        _rreq_rrep_address_consumer_entries,
+                                        ARRAYSIZE(_rreq_rrep_address_consumer_entries));
+    rfc5444_reader_add_message_consumer(&reader, &_rrep_address_consumer,
+                                        _rreq_rrep_address_consumer_entries,
+                                        ARRAYSIZE(_rreq_rrep_address_consumer_entries));
+    rfc5444_reader_add_message_consumer(&reader, &_rerr_address_consumer,
+                                        _rerr_address_consumer_entries,
+                                        ARRAYSIZE(_rerr_address_consumer_entries));
+}
+
+void aodv_packet_reader_cleanup(void)
+{
+    VDEBUG("%s()\n", __func__);
+    rfc5444_reader_cleanup(&reader);
+}
+
+int aodv_packet_reader_handle_packet(void *buffer, size_t length, struct netaddr *sender)
+{
+    AODV_DEBUG("%s()\n", __func__);
+    memcpy(&packet_data.sender, sender, sizeof(*sender));
+    DEBUG("\t sender: %s\n", netaddr_to_string(&nbuf, &packet_data.sender));
+
+    return rfc5444_reader_handle_packet(&reader, buffer, length);
+}
+
+/*============= HELPER FUNCTIONS =============================================*/
+
+/*
+ * Cost(L): Get Cost of a Link regarding the specified metric.
+ * (currently only AODVV2_DEFAULT_METRIC_TYPE (HopCt) implemented)
+ * returns cost if metric is known, NULL otherwise
+ */
+static uint8_t _get_link_cost(aodvv2_metric_t metricType)
+{
+    if (metricType == AODVV2_DEFAULT_METRIC_TYPE) {
+        return 1;
+    }
+    return 0;
+}
+
+/*
+ * MAX_METRIC[MetricType]:
+ * returns maximum value of the given metric if metric is known, NULL otherwise.
+ */
+static uint8_t _get_max_metric(aodvv2_metric_t metricType)
+{
+    if (metricType == AODVV2_DEFAULT_METRIC_TYPE) {
+        return AODVV2_MAX_HOPCOUNT;
+    }
+    return 0;
+}
+
+/*
+ * Calculate a metric's new value according to the specified MetricType
+ * (currently only implemented for AODVV2_DEFAULT_METRIC_TYPE (HopCt))
+ */
+static void _update_metric(aodvv2_metric_t metricType, uint8_t *metric)
+{
+    if (metricType == AODVV2_DEFAULT_METRIC_TYPE){
+        *metric = *metric + 1;
+    }
+}
diff --git a/sys/net/routing/aodvv2/reader.h b/sys/net/routing/aodvv2/reader.h
new file mode 100644
index 0000000000..07fe133843
--- /dev/null
+++ b/sys/net/routing/aodvv2/reader.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        reader.h
+ * @brief       reading and handling of RFC5444 aodvv2 messages
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#ifndef AODVV2_READER_H_
+#define AODVV2_READER_H_
+
+#include <string.h>
+#include <stdio.h>
+
+#include "common/netaddr.h"
+#include "rfc5444/rfc5444_reader.h"
+
+#include "utils.h"
+#include "routingtable.h"
+#include "constants.h"
+#include "seqnum.h"
+#include "aodv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Initialize reader.
+ */
+void aodv_packet_reader_init(void);
+
+/**
+ * @brief Clean up after reader. Only needs to be called upon shutdown.
+ */
+void aodv_packet_reader_cleanup(void);
+
+/**
+ * @brief Read data buffer as RFC5444 packet and handle the data it contains
+ *
+ * @param[in] buffer  Data to be read and handled
+ * @param[in] length  Length of data
+ * @param[in] sender  Address of the node from which the packet was received
+ */
+int aodv_packet_reader_handle_packet(void *buffer, size_t length, struct netaddr *sender);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* AODVV2_READER_H_ */
diff --git a/sys/net/routing/aodvv2/routingtable.c b/sys/net/routing/aodvv2/routingtable.c
new file mode 100644
index 0000000000..e7a7316ef2
--- /dev/null
+++ b/sys/net/routing/aodvv2/routingtable.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        routing.c
+ * @brief       Cobbled-together routing table.
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#include "debug.h"
+
+#include "routingtable.h"
+#include "aodv_debug.h"
+
+#define ENABLE_DEBUG (0)
+
+/* helper functions */
+static void _reset_entry_if_stale(uint8_t i);
+
+static struct aodvv2_routing_entry_t routing_table[AODVV2_MAX_ROUTING_ENTRIES];
+static timex_t null_time, max_seqnum_lifetime, active_interval, max_idletime, validity_t;
+timex_t now;
+#ifdef DEBUG_ENABLED
+static struct netaddr_str nbuf;
+#endif
+
+void routingtable_init(void)
+{
+    null_time = timex_set(0, 0);
+    max_seqnum_lifetime = timex_set(AODVV2_MAX_SEQNUM_LIFETIME, 0);
+    active_interval = timex_set(AODVV2_ACTIVE_INTERVAL, 0);
+    max_idletime = timex_set(AODVV2_MAX_IDLETIME, 0);
+    validity_t = timex_set(AODVV2_ACTIVE_INTERVAL + AODVV2_MAX_IDLETIME, 0);
+
+    memset(&routing_table, 0, sizeof(routing_table));
+    AODV_DEBUG("routing table initialized.\n");
+}
+
+struct netaddr *routingtable_get_next_hop(struct netaddr *dest, aodvv2_metric_t metricType)
+{
+    struct aodvv2_routing_entry_t *entry = routingtable_get_entry(dest, metricType);
+    if (!entry) {
+        return NULL;
+    }
+    return (&entry->nextHopAddr);
+}
+
+void routingtable_add_entry(struct aodvv2_routing_entry_t *entry)
+{
+    /* only add if we don't already know the address */
+    if (routingtable_get_entry(&(entry->addr), entry->metricType)) {
+        return;
+    }
+    /*find free spot in RT and place rt_entry there */
+    for (unsigned i = 0; i < AODVV2_MAX_ROUTING_ENTRIES; i++) {
+        if (routing_table[i].addr._type == AF_UNSPEC) {
+            memcpy(&routing_table[i], entry, sizeof(struct aodvv2_routing_entry_t));
+            return;
+        }
+    }
+}
+
+struct aodvv2_routing_entry_t *routingtable_get_entry(struct netaddr *addr,
+                                                      aodvv2_metric_t metricType)
+{
+    for (unsigned i = 0; i < AODVV2_MAX_ROUTING_ENTRIES; i++) {
+        _reset_entry_if_stale(i);
+
+        if (!netaddr_cmp(&routing_table[i].addr, addr)
+            && routing_table[i].metricType == metricType) {
+            DEBUG("[routing] found entry for %s :", netaddr_to_string(&nbuf, addr));
+#ifdef DEBUG_ENABLED
+            print_routingtable_entry(&routing_table[i]);
+#endif
+            return &routing_table[i];
+        }
+    }
+    return NULL;
+}
+
+void routingtable_delete_entry(struct netaddr *addr, aodvv2_metric_t metricType)
+{
+    for (unsigned i = 0; i < AODVV2_MAX_ROUTING_ENTRIES; i++) {
+        _reset_entry_if_stale(i);
+
+        if (!netaddr_cmp(&routing_table[i].addr, addr)
+                && routing_table[i].metricType == metricType) {
+            memset(&routing_table[i], 0, sizeof(routing_table[i]));
+            return;
+        }
+    }
+}
+
+void routingtable_break_and_get_all_hopping_over(struct netaddr *hop,
+                                                 struct unreachable_node unreachable_nodes[],
+                                                 size_t *len)
+{
+    *len = 0; /* to be sure */
+
+    for (unsigned i = 0; i < AODVV2_MAX_ROUTING_ENTRIES; i++) {
+        _reset_entry_if_stale(i);
+
+        if (netaddr_cmp(&routing_table[i].nextHopAddr, hop) == 0) {
+            if (routing_table[i].state == ROUTE_STATE_ACTIVE &&
+                    *len < AODVV2_MAX_UNREACHABLE_NODES) {
+                /* when the max number of unreachable nodes is reached we're screwed.
+                 * the above check is just damage control. */
+                unreachable_nodes[*len].addr = routing_table[i].addr;
+                unreachable_nodes[*len].seqnum = routing_table[i].seqnum;
+
+                (*len)++;
+                DEBUG("\t[routing] unreachable node found: %s\n", netaddr_to_string(&nbuf, &routing_table[i].nextHopAddr));
+            }
+            routing_table[i].state = ROUTE_STATE_BROKEN;
+            DEBUG("\t[routing] number of unreachable nodes: %i\n", *len);
+        }
+    }
+}
+
+/*
+ * Check if entry at index i is stale as described in Section 6.3.
+ * and clear the struct it fills if it is
+ */
+static void _reset_entry_if_stale(uint8_t i)
+{
+    vtimer_now(&now);
+    timex_t lastUsed, expirationTime;
+
+    if (timex_cmp(routing_table[i].expirationTime, null_time) == 0) {
+        return;
+    }
+
+    int state = routing_table[i].state;
+    lastUsed = routing_table[i].lastUsed;
+    expirationTime = routing_table[i].expirationTime;
+
+    /* an Active route is considered to remain Active as long as it is used at least once
+     * during every ACTIVE_INTERVAL. When a route is no longer Active, it becomes an Idle route. */
+
+    /* if the node is younger than the active interval, don't bother */
+    if (timex_cmp(now, active_interval) < 0) {
+        return;
+    }
+
+    if ((state == ROUTE_STATE_ACTIVE) &&
+        (timex_cmp(timex_sub(now, active_interval), lastUsed) == 1)) {
+        DEBUG("\t[routing] route towards %s Idle\n",
+              netaddr_to_string(&nbuf, &routing_table[i].addr));
+        routing_table[i].state = ROUTE_STATE_IDLE;
+        routing_table[i].lastUsed = now; /* mark the time entry was set to Idle */
+    }
+
+    /* After an idle route remains Idle for MAX_IDLETIME, it becomes an Expired route.
+       A route MUST be considered Expired if Current_Time >= Route.ExpirationTime
+    */
+
+    /* if the node is younger than the expiration time, don't bother */
+    if (timex_cmp(now, expirationTime) < 0) {
+        return;
+    }
+
+    if ((state == ROUTE_STATE_IDLE) &&
+        (timex_cmp(expirationTime, now) < 1)) {
+        DEBUG("\t[routing] route towards %s Expired\n",
+              netaddr_to_string(&nbuf, &routing_table[i].addr));
+        DEBUG("\t expirationTime: %"PRIu32":%"PRIu32" , now: %"PRIu32":%"PRIu32"\n",
+              expirationTime.seconds, expirationTime.microseconds,
+              now.seconds, now.microseconds);
+        routing_table[i].state = ROUTE_STATE_EXPIRED;
+        routing_table[i].lastUsed = now; /* mark the time entry was set to Expired */
+    }
+
+    /* After that time, old sequence number information is considered no longer
+     * valuable and the Expired route MUST BE expunged */
+    if (timex_cmp(timex_sub(now, lastUsed), max_seqnum_lifetime) >= 0) {
+        DEBUG("\t[routing] reset routing table entry for %s at %i\n",
+              netaddr_to_string(&nbuf, &routing_table[i].addr), i);
+        memset(&routing_table[i], 0, sizeof(routing_table[i]));
+    }
+}
+
+bool routingtable_offers_improvement(struct aodvv2_routing_entry_t *rt_entry,
+                                     struct node_data *node_data)
+{
+    /* Check if new info is stale */
+    if (seqnum_cmp(node_data->seqnum, rt_entry->seqnum) == -1) {
+        return false;
+    }
+    /* Check if new info is more costly */
+    if ((node_data->metric >= rt_entry->metric)
+        && !(rt_entry->state != ROUTE_STATE_BROKEN)) {
+        return false;
+    }
+    /* Check if new info repairs a broken route */
+    if (!(rt_entry->state != ROUTE_STATE_BROKEN)) {
+        return false;
+    }
+    return true;
+}
+
+void routingtable_fill_routing_entry_t_rreq(struct aodvv2_packet_data *packet_data,
+                                            struct aodvv2_routing_entry_t *rt_entry,
+                                            uint8_t link_cost)
+{
+    rt_entry->addr = packet_data->origNode.addr;
+    rt_entry->seqnum = packet_data->origNode.seqnum;
+    rt_entry->nextHopAddr = packet_data->sender;
+    rt_entry->lastUsed = packet_data->timestamp;
+    rt_entry->expirationTime = timex_add(packet_data->timestamp, validity_t);
+    rt_entry->metricType = packet_data->metricType;
+    rt_entry->metric = packet_data->origNode.metric + link_cost;
+    rt_entry->state = ROUTE_STATE_ACTIVE;
+}
+
+void routingtable_fill_routing_entry_t_rrep(struct aodvv2_packet_data *packet_data,
+                                            struct aodvv2_routing_entry_t *rt_entry,
+                                            uint8_t link_cost)
+{
+    rt_entry->addr = packet_data->targNode.addr;
+    rt_entry->seqnum = packet_data->targNode.seqnum;
+    rt_entry->nextHopAddr = packet_data->sender;
+    rt_entry->lastUsed = packet_data->timestamp;
+    rt_entry->expirationTime = timex_add(packet_data->timestamp, validity_t);
+    rt_entry->metricType = packet_data->metricType;
+    rt_entry->metric = packet_data->targNode.metric + link_cost;
+    rt_entry->state = ROUTE_STATE_ACTIVE;
+}
+
+void print_routingtable(void)
+{
+    printf("===== BEGIN ROUTING TABLE ===================\n");
+    for (int i = 0; i < AODVV2_MAX_ROUTING_ENTRIES; i++) {
+        /* route has been used before => non-empty entry */
+        if (routing_table[i].lastUsed.seconds
+            || routing_table[i].lastUsed.microseconds) {
+            print_routingtable_entry(&routing_table[i]);
+        }
+    }
+    printf("===== END ROUTING TABLE =====================\n");
+}
+
+void print_routingtable_entry(struct aodvv2_routing_entry_t *rt_entry)
+{
+    struct netaddr_str nbuf;
+
+    printf(".................................\n");
+    printf("\t address: %s\n", netaddr_to_string(&nbuf, &(rt_entry->addr)));
+    printf("\t seqnum: %i\n", rt_entry->seqnum);
+    printf("\t nextHopAddress: %s\n",
+            netaddr_to_string(&nbuf, &(rt_entry->nextHopAddr)));
+    printf("\t lastUsed: %"PRIu32":%"PRIu32"\n",
+            rt_entry->lastUsed.seconds, rt_entry->lastUsed.microseconds);
+    printf("\t expirationTime: %"PRIu32":%"PRIu32"\n",
+            rt_entry->expirationTime.seconds, rt_entry->expirationTime.microseconds);
+    printf("\t metricType: %i\n", rt_entry->metricType);
+    printf("\t metric: %d\n", rt_entry->metric);
+    printf("\t state: %d\n", rt_entry->state);
+}
diff --git a/sys/net/routing/aodvv2/routingtable.h b/sys/net/routing/aodvv2/routingtable.h
new file mode 100644
index 0000000000..5e1e533aaa
--- /dev/null
+++ b/sys/net/routing/aodvv2/routingtable.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        routingtable.h
+ * @brief       Cobbled-together routing table.
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#ifndef AODVV2_ROUTINGTABLE_H_
+#define AODVV2_ROUTINGTABLE_H_
+
+#include <string.h>
+
+#include "common/netaddr.h"
+
+#include "aodvv2/types.h"
+#include "constants.h"
+#include "seqnum.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * A route table entry (i.e., a route) may be in one of the following states:
+ */
+enum aodvv2_routing_states
+{
+    ROUTE_STATE_ACTIVE,
+    ROUTE_STATE_IDLE,
+    ROUTE_STATE_EXPIRED,
+    ROUTE_STATE_BROKEN,
+    ROUTE_STATE_TIMED
+};
+
+/**
+ * all fields of a routing table entry
+ */
+struct aodvv2_routing_entry_t
+{
+    struct netaddr addr;                /**< IP address of this route's destination */
+    aodvv2_seqnum_t seqnum;             /**< The Sequence Number obtained from the
+                                         *   last packet that updated the entry */
+    struct netaddr nextHopAddr;         /**< IP address of the the next hop towards
+                                         *   the destination */
+    timex_t lastUsed;                   /**< IP address of this route's destination */
+    timex_t expirationTime;             /**< Time at which this route expires */
+    aodvv2_metric_t metricType;         /**< Metric type of this route */
+    uint8_t metric;                     /**< Metric value of this route*/
+    uint8_t state;                      /**< State of this route
+                                         *   (i.e. one of aodvv2_routing_states) */
+};
+
+/**
+ * @brief     Initialize routing table.
+ */
+void routingtable_init(void);
+
+/**
+ * @brief     Get next hop towards dest.
+ *            Returns NULL if dest is not in routing table.
+ *
+ * @param[in] dest        Destination of the packet
+ * @param[in] metricType  Metric Type of the desired route
+ * @return                next hop towards dest if it exists, NULL otherwise
+ */
+struct netaddr *routingtable_get_next_hop(struct netaddr *dest, aodvv2_metric_t metricType);
+
+/**
+ * @brief     Add new entry to routing table, if there is no other entry
+ *            to the same destination.
+ *
+ * @param[in] entry        The routing table entry to add
+ */
+void routingtable_add_entry(struct aodvv2_routing_entry_t *entry);
+
+/**
+ * @brief     Retrieve pointer to a routing table entry.
+ *            To edit, simply follow the pointer.
+ *            Returns NULL if addr is not in routing table.
+ *
+ * @param[in] addr          The address towards which the route should point
+ * @param[in] metricType    Metric Type of the desired route
+ * @return                  Routing table entry if it exists, NULL otherwise
+ */
+struct aodvv2_routing_entry_t *routingtable_get_entry(struct netaddr *addr, aodvv2_metric_t metricType);
+
+/**
+ * @brief     Delete routing table entry towards addr with metric type MetricType,
+ *            if it exists.
+ *
+ * @param[in] addr          The address towards which the route should point
+ * @param[in] metricType    Metric Type of the desired route
+ */
+void routingtable_delete_entry(struct netaddr *addr, aodvv2_metric_t metricType);
+
+/**
+ * Find all routing table entries that use hop as their nextHopAddress, mark them
+ * as broken, write the active one into unreachable_nodes[] and increment len
+ * accordingly. (Sorry about the Name.)
+ *
+ * @param hop                 Address of the newly unreachable next hop
+ * @param unreachable_nodes[] array of newlu unreachable nodes to be filled.
+ *                            should be empty.
+ * @param len                 size_t* which will contain the length of
+ *                            unreachable_nodes[] after execution
+ */
+void routingtable_break_and_get_all_hopping_over(struct netaddr *hop,
+                                                 struct unreachable_node unreachable_nodes[],
+                                                 size_t *len);
+
+/**
+ * Check if the data of a RREQ or RREP offers improvement for an existing routing
+ * table entry.
+ * @param rt_entry            the routing table entry to check
+ * @param node_data           The data to check against. When handling a RREQ,
+ *                            the OrigNode's information (i.e. packet_data.origNode)
+ *                            must be passed. When handling a RREP, the
+ *                            TargNode's data (i.e. packet_data.targNode) must
+ *                            be passed.
+ */
+bool routingtable_offers_improvement(struct aodvv2_routing_entry_t *rt_entry,
+                                     struct node_data *node_data);
+
+/**
+ * Fills a routing table entry with the data of a RREQ.
+ * @param packet_data         the RREQ's data
+ * @param rt_entry            the routing table entry to fill
+ * @param link_cost           the link cost for this RREQ
+ */
+void routingtable_fill_routing_entry_t_rreq(struct aodvv2_packet_data *packet_data,
+                                            struct aodvv2_routing_entry_t *rt_entry,
+                                            uint8_t link_cost);
+
+/**
+ * Fills a routing table entry with the data of a RREP.
+ * @param packet_data         the RREP's data
+ * @param rt_entry            the routing table entry to fill
+ * @param link_cost           the link cost for this RREP
+ */
+void routingtable_fill_routing_entry_t_rrep(struct aodvv2_packet_data *packet_data,
+                                            struct aodvv2_routing_entry_t *rt_entry,
+                                            uint8_t link_cost);
+
+void print_routingtable(void);
+void print_routingtable_entry(struct aodvv2_routing_entry_t *rt_entry);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AODVV2_ROUTINGTABLE_H_*/
diff --git a/sys/net/routing/aodvv2/seqnum.c b/sys/net/routing/aodvv2/seqnum.c
new file mode 100644
index 0000000000..d6da9ae314
--- /dev/null
+++ b/sys/net/routing/aodvv2/seqnum.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        seqnum.c
+ * @brief       aodvv2 sequence number
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#include "debug.h"
+
+#include "seqnum.h"
+
+#define ENABLE_DEBUG (0)
+
+static aodvv2_seqnum_t seqnum;
+
+void seqnum_init(void)
+{
+    seqnum = 1;
+}
+
+void seqnum_inc(void)
+{
+    if (seqnum == 65535) {
+        seqnum = 1;
+    }
+    else if (seqnum == 0) {
+        DEBUG("ERROR: SeqNum shouldn't be 0! \n"); /* TODO handle properly */
+    }
+    else {
+        seqnum++;
+    }
+}
+
+aodvv2_seqnum_t seqnum_get(void)
+{
+    return seqnum;
+}
diff --git a/sys/net/routing/aodvv2/seqnum.h b/sys/net/routing/aodvv2/seqnum.h
new file mode 100644
index 0000000000..0a11a44b6c
--- /dev/null
+++ b/sys/net/routing/aodvv2/seqnum.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        seqnum.h
+ * @brief       aodvv2 sequence number
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#ifndef AODVV2_SEQNUM_H_
+#define AODVV2_SEQNUM_H_
+
+#include <stdint.h>
+
+#include "aodvv2/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Initialize sequence number.
+ */
+void seqnum_init(void);
+
+/**
+ * @brief Get sequence number.
+ *
+ * @return sequence number
+ */
+aodvv2_seqnum_t seqnum_get(void);
+
+/**
+ * @brief Increment the sequence number by 1.
+ */
+void seqnum_inc(void);
+
+/**
+ * @brief Compare 2 sequence numbers.
+ * @param[in] s1  first sequence number
+ * @param[in] s2  second sequence number
+ * @return        -1 when s1 is smaller, 0 if equal, 1 if s1 is bigger.
+ */
+static inline int seqnum_cmp(aodvv2_seqnum_t s1, aodvv2_seqnum_t s2)
+{
+    return s1 == s2 ? 0 : (s1 > s2 ? +1 : -1);
+}
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* AODVV2_SEQNUM_H_ */
diff --git a/sys/net/routing/aodvv2/utils.c b/sys/net/routing/aodvv2/utils.c
new file mode 100644
index 0000000000..de48d13442
--- /dev/null
+++ b/sys/net/routing/aodvv2/utils.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        utils.c
+ * @brief       client- and RREQ-table, ipv6 address representation converters
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#include "utils.h"
+#include "debug.h"
+
+#include "aodv_debug.h"
+
+#define ENABLE_DEBUG (0)
+
+/* Some aodvv2 utilities (mostly tables) */
+static mutex_t clientt_mutex;
+static mutex_t rreqt_mutex;
+
+/* helper functions */
+static struct aodvv2_rreq_entry *_get_comparable_rreq(struct aodvv2_packet_data *packet_data);
+static void _add_rreq(struct aodvv2_packet_data *packet_data);
+static void _reset_entry_if_stale(uint8_t i);
+
+static struct netaddr client_table[AODVV2_MAX_CLIENTS];
+static struct aodvv2_rreq_entry rreq_table[AODVV2_RREQ_BUF];
+
+#ifdef DEBUG_ENABLED
+static struct netaddr_str nbuf;
+#endif
+static timex_t null_time, now, _max_idletime;
+
+
+void clienttable_init(void)
+{
+    mutex_lock(&clientt_mutex);
+    memset(&client_table, 0, sizeof(client_table));
+    mutex_unlock(&clientt_mutex);
+
+    AODV_DEBUG("client table initialized.\n");
+}
+
+void clienttable_add_client(struct netaddr *addr)
+{
+    if (clienttable_is_client(addr)){
+        return;
+    }
+
+    /*find free spot in client table and place client address there */
+    mutex_lock(&clientt_mutex);
+    for (unsigned i = 0; i < AODVV2_MAX_CLIENTS; i++) {
+        if ((client_table[i]._type == AF_UNSPEC) &&
+            (client_table[i]._prefix_len == 0)) {
+            client_table[i] = *addr;
+            AODV_DEBUG("clienttable: added client %s\n",
+                  netaddr_to_string(&nbuf, addr));
+            mutex_unlock(&clientt_mutex);
+            return;
+        }
+    }
+    AODV_DEBUG("Error: Client could not be added: Client table is full.\n");
+    mutex_unlock(&clientt_mutex);
+}
+
+bool clienttable_is_client(struct netaddr *addr)
+{
+    mutex_lock(&clientt_mutex);
+    for (unsigned i = 0; i < AODVV2_MAX_CLIENTS; i++) {
+        if (!netaddr_cmp(&client_table[i], addr)) {
+            mutex_unlock(&clientt_mutex);
+            return true;
+        }
+    }
+    mutex_unlock(&clientt_mutex);
+    return false;
+}
+
+void clienttable_delete_client(struct netaddr *addr)
+{
+    if (!clienttable_is_client(addr)) {
+        return;
+    }
+
+    mutex_lock(&clientt_mutex);
+    for (unsigned i = 0; i < AODVV2_MAX_CLIENTS; i++) {
+        if (!netaddr_cmp(&client_table[i], addr)) {
+            memset(&client_table[i], 0, sizeof(client_table[i]));
+            mutex_unlock(&clientt_mutex);
+            return;
+        }
+    }
+}
+
+void rreqtable_init(void)
+{
+    mutex_lock(&rreqt_mutex);
+    null_time = timex_set(0, 0);
+    _max_idletime = timex_set(AODVV2_MAX_IDLETIME, 0);
+
+    memset(&rreq_table, 0, sizeof(rreq_table));
+    mutex_unlock(&rreqt_mutex);
+    AODV_DEBUG("RREQ table initialized.\n");
+}
+
+bool rreqtable_is_redundant(struct aodvv2_packet_data *packet_data)
+{
+    struct aodvv2_rreq_entry *comparable_rreq;
+    timex_t now;
+    bool result;
+
+    mutex_lock(&rreqt_mutex);
+    comparable_rreq = _get_comparable_rreq(packet_data);
+
+    /* if there is no comparable rreq stored, add one and return false */
+    if (comparable_rreq == NULL) {
+        _add_rreq(packet_data);
+        result = false;
+    }
+    else {
+        int seqnum_comparison = seqnum_cmp(packet_data->origNode.seqnum, comparable_rreq->seqnum);
+
+        /*
+         * If two RREQs have the same
+         * metric type and OrigNode and Targnode addresses, the information from
+         * the one with the older Sequence Number is not needed in the table
+         */
+        if (seqnum_comparison == -1) {
+            result = true;
+        }
+
+        if (seqnum_comparison == 1) {
+            /* Update RREQ table entry with new seqnum value */
+            comparable_rreq->seqnum = packet_data->origNode.seqnum;
+        }
+
+        /*
+         * in case they have the same Sequence Number, the one with the greater
+         * Metric value is not needed
+         */
+        if (seqnum_comparison == 0) {
+            if (comparable_rreq->metric <= packet_data->origNode.metric) {
+                result = true;
+            }
+            /* Update RREQ table entry with new metric value */
+            comparable_rreq->metric = packet_data->origNode.metric;
+        }
+
+        /* Since we've changed RREQ info, update the timestamp */
+        vtimer_now(&now);
+        comparable_rreq->timestamp = now;
+        result = true;
+    }
+
+    mutex_unlock(&rreqt_mutex);
+    return result;
+}
+
+/*
+ * retrieve pointer to a comparable (according to Section 6.7.)
+ * RREQ table entry if it exists and NULL otherwise.
+ * Two AODVv2 RREQ messages are comparable if:
+ * - they have the same metric type
+ * - they have the same OrigNode and TargNode addresses
+ */
+static struct aodvv2_rreq_entry *_get_comparable_rreq(struct aodvv2_packet_data *packet_data)
+{
+    for (unsigned i = 0; i < AODVV2_RREQ_BUF; i++) {
+        _reset_entry_if_stale(i);
+
+        if (!netaddr_cmp(&rreq_table[i].origNode, &packet_data->origNode.addr)
+                && !netaddr_cmp(&rreq_table[i].targNode, &packet_data->targNode.addr)
+                && rreq_table[i].metricType == packet_data->metricType) {
+            return &rreq_table[i];
+        }
+    }
+
+    return NULL;
+}
+
+
+static void _add_rreq(struct aodvv2_packet_data *packet_data)
+{
+    if (_get_comparable_rreq(packet_data)) {
+        return;
+    }
+    /*find empty rreq and fill it with packet_data */
+
+    for (unsigned i = 0; i < AODVV2_RREQ_BUF; i++) {
+        if (!rreq_table[i].timestamp.seconds &&
+            !rreq_table[i].timestamp.microseconds) {
+            rreq_table[i].origNode = packet_data->origNode.addr;
+            rreq_table[i].targNode = packet_data->targNode.addr;
+            rreq_table[i].metricType = packet_data->metricType;
+            rreq_table[i].metric = packet_data->origNode.metric;
+            rreq_table[i].seqnum = packet_data->origNode.seqnum;
+            rreq_table[i].timestamp = packet_data->timestamp;
+            return;
+        }
+    }
+}
+
+/*
+ * Check if entry at index i is stale and clear the struct it fills if it is
+ */
+static void _reset_entry_if_stale(uint8_t i)
+{
+    vtimer_now(&now);
+
+    if (timex_cmp(rreq_table[i].timestamp, null_time) == 0) {
+        return;
+    }
+    timex_t expiration_time = timex_add(rreq_table[i].timestamp, _max_idletime);
+    if (timex_cmp(expiration_time, now) < 0) {
+        /* timestamp+expiration time is in the past: this entry is stale */
+        DEBUG("\treset rreq table entry %s\n",
+              netaddr_to_string(&nbuf, &rreq_table[i].origNode));
+        memset(&rreq_table[i], 0, sizeof(rreq_table[i]));
+    }
+}
+
+void ipv6_addr_t_to_netaddr(ipv6_addr_t *src, struct netaddr *dst)
+{
+    dst->_type = AF_INET6;
+    dst->_prefix_len = AODVV2_RIOT_PREFIXLEN;
+    memcpy(dst->_addr, src, sizeof(dst->_addr));
+}
+
+void netaddr_to_ipv6_addr_t(struct netaddr *src, ipv6_addr_t *dst)
+{
+    memcpy(dst, src->_addr, sizeof(uint8_t) * NETADDR_MAX_LENGTH);
+}
diff --git a/sys/net/routing/aodvv2/utils.h b/sys/net/routing/aodvv2/utils.h
new file mode 100644
index 0000000000..8baee0e1f9
--- /dev/null
+++ b/sys/net/routing/aodvv2/utils.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        utils.h
+ * @brief       client- and RREQ-table, ipv6 address representation converters
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#ifndef AODVV2_UTILS_H_
+#define AODVV2_UTILS_H_
+
+#include <stdio.h>
+
+#include "ipv6.h"
+
+#include "common/netaddr.h"
+
+#include "aodvv2/types.h"
+#include "constants.h"
+#include "seqnum.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define AODVV2_MAX_CLIENTS 1        /** multiple clients are currently not supported. */
+#define AODVV2_RREQ_BUF 128         /** should be enough for now... */
+#define AODVV2_RREQ_WAIT_TIME 2     /** seconds */
+#define AODVV2_RIOT_PREFIXLEN  128  /** Prefix length of the IPv6 addresses
+                                     *  used in the network served by AODVv2 () */
+/**
+ * @brief   RREQ Table entry which stores all information about a RREQ that was received
+ *          in order to avoid duplicates.
+ */
+struct aodvv2_rreq_entry
+{
+    struct netaddr origNode;            /**< Node which originated the RREQ*/
+    struct netaddr targNode;            /**< Target (destination) of the RREQ */
+    aodvv2_metric_t metricType;         /**< Metric type of the RREQ */
+    uint8_t metric;                     /**< Metric of the RREQ */
+    aodvv2_seqnum_t seqnum;             /**< Sequence number of the RREQ */
+    timex_t timestamp;                  /**< Last time this entry was updated */
+};
+
+/**
+ * Initialize table of clients that the router currently serves.
+ */
+void clienttable_init(void);
+
+/**
+ * Add client to the list of clients that the router currently serves.
+ * @param addr      address of the client
+ *                  (Since the current version doesn't offer support for
+ *                  Client Networks, the prefixlen is currently ignored.)
+ */
+void clienttable_add_client(struct netaddr *addr);
+
+/**
+ * Find out if a client is in the list of clients that the router currently serves.
+ * @param addr      address of the client in question
+ *                  (Since the current version doesn't offer support for
+ *                  Client Networks, the prefixlen is currently ignored.)
+ */
+bool clienttable_is_client(struct netaddr *addr);
+
+/**
+ * Delete a client from the list of clients that the router currently serves.
+ * @param addr      address of the client to delete
+ *                  (Since the current version doesn't offer support for
+ *                  Client Networks, the prefixlen is currently ignored.)
+ */
+void clienttable_delete_client(struct netaddr *addr);
+
+/**
+ * Initialize RREQ table.
+ */
+void rreqtable_init(void);
+
+/**
+ * Check if a RREQ is redundant, i.e. was received from another node already.
+ * Behaves as described in Sections 5.7. and 7.6.
+ * @param packet_data  data of the RREQ in question
+ */
+bool rreqtable_is_redundant(struct aodvv2_packet_data *packet_data);
+
+/**
+ * Convert an IP stored as an ipv6_addr_t to a netaddr
+ * @param src       ipv6_addr_t to convert
+ * @param dst       (empty) netaddr to convert into
+ */
+void ipv6_addr_t_to_netaddr(ipv6_addr_t *src, struct netaddr *dst);
+
+/**
+ * Convert an IP stored as a netaddr to an ipv6_addr_t
+ * @param src       (empty) netaddr to convert into
+ * @param dst       ipv6_addr_t to convert
+ */
+void netaddr_to_ipv6_addr_t(struct netaddr *src, ipv6_addr_t *dst);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* AODVV2_UTILS_H_ */
diff --git a/sys/net/routing/aodvv2/writer.c b/sys/net/routing/aodvv2/writer.c
new file mode 100644
index 0000000000..79acc8e024
--- /dev/null
+++ b/sys/net/routing/aodvv2/writer.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        writer.c
+ * @brief       writer to create RFC5444 aodvv2 messages
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#ifdef RIOT
+#include "net_help.h"
+#endif
+
+#include "writer.h"
+#include "debug.h"
+
+#include "aodv_debug.h"
+
+#define ENABLE_DEBUG (0)
+
+static void _cb_addMessageHeader(struct rfc5444_writer *wr,
+                                 struct rfc5444_writer_message *message);
+
+static void _cb_rreq_addAddresses(struct rfc5444_writer *wr);
+static void _cb_rrep_addAddresses(struct rfc5444_writer *wr);
+static void _cb_rerr_addAddresses(struct rfc5444_writer *wr);
+
+static mutex_t writer_mutex;
+
+struct rfc5444_writer writer;
+static struct writer_target _target;
+
+static struct unreachable_node *_unreachable_nodes;
+static size_t _num_unreachable_nodes;
+
+static uint8_t _msg_buffer[128];
+static uint8_t _msg_addrtlvs[1000];
+static uint8_t _packet_buffer[128];
+
+static struct rfc5444_writer_message *_rreq_msg;
+static struct rfc5444_writer_message *_rrep_msg;
+static struct rfc5444_writer_message *_rerr_msg;
+
+/*
+ * message content provider that will add message TLVs,
+ * addresses and address block TLVs to all messages of type RREQ.
+ */
+static struct rfc5444_writer_content_provider _rreq_message_content_provider =
+{
+    .msg_type = RFC5444_MSGTYPE_RREQ,
+    .addAddresses = _cb_rreq_addAddresses,
+};
+
+/* declaration of all address TLVs added to the RREQ message */
+static struct rfc5444_writer_tlvtype _rreq_addrtlvs[] =
+{
+    [RFC5444_MSGTLV_ORIGSEQNUM] = { .type = RFC5444_MSGTLV_ORIGSEQNUM },
+    [RFC5444_MSGTLV_METRIC] = {
+        .type = RFC5444_MSGTLV_METRIC,
+        .exttype = AODVV2_DEFAULT_METRIC_TYPE
+    },
+};
+
+/*
+ * message content provider that will add message TLVs,
+ * addresses and address block TLVs to all messages of type RREQ.
+ */
+static struct rfc5444_writer_content_provider _rrep_message_content_provider =
+{
+    .msg_type = RFC5444_MSGTYPE_RREP,
+    .addAddresses = _cb_rrep_addAddresses,
+};
+
+/* declaration of all address TLVs added to the RREP message */
+static struct rfc5444_writer_tlvtype _rrep_addrtlvs[] =
+{
+    [RFC5444_MSGTLV_ORIGSEQNUM] = { .type = RFC5444_MSGTLV_ORIGSEQNUM},
+    [RFC5444_MSGTLV_TARGSEQNUM] = { .type = RFC5444_MSGTLV_TARGSEQNUM},
+    [RFC5444_MSGTLV_METRIC] = {
+        .type = RFC5444_MSGTLV_METRIC,
+        .exttype = AODVV2_DEFAULT_METRIC_TYPE
+    },
+};
+
+/*
+ * message content provider that will add message TLVs,
+ * addresses and address block TLVs to all messages of type RREQ.
+ */
+static struct rfc5444_writer_content_provider _rerr_message_content_provider =
+{
+    .msg_type = RFC5444_MSGTYPE_RERR,
+    .addAddresses = _cb_rerr_addAddresses,
+};
+
+/* declaration of all address TLVs added to the RREP message */
+static struct rfc5444_writer_tlvtype _rerr_addrtlvs[] =
+{
+    [RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM] = { .type = RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM},
+};
+
+/**
+ * Callback to define the message header for a RFC5444 RREQ message
+ * @param message
+ */
+static void
+_cb_addMessageHeader(struct rfc5444_writer *wr, struct rfc5444_writer_message *message)
+{
+    AODV_DEBUG("%s()\n", __func__);
+
+    /* no originator, no hopcount, has hoplimit, no seqno */
+    rfc5444_writer_set_msg_header(wr, message, false, false, true, false);
+    rfc5444_writer_set_msg_hoplimit(wr, message, _target.packet_data.hoplimit);
+}
+
+/**
+ * Callback to add addresses and address TLVs to a RFC5444 RREQ message
+ */
+static void
+_cb_rreq_addAddresses(struct rfc5444_writer *wr)
+{
+    AODV_DEBUG("%s()\n", __func__);
+
+    struct rfc5444_writer_address *origNode_addr;
+
+    /* add origNode address (has no address tlv); is mandatory address */
+    origNode_addr = rfc5444_writer_add_address(wr, _rreq_message_content_provider.creator,
+                    &_target.packet_data.origNode.addr, true);
+
+    /* add targNode address (has no address tlv); is mandatory address */
+    rfc5444_writer_add_address(wr, _rreq_message_content_provider.creator,
+                               &_target.packet_data.targNode.addr, true);
+
+    /* add SeqNum TLV and metric TLV to origNode */
+    /* TODO: allow_dup true or false? */
+    rfc5444_writer_add_addrtlv(wr, origNode_addr, &_rreq_addrtlvs[RFC5444_MSGTLV_ORIGSEQNUM],
+                               &_target.packet_data.origNode.seqnum,
+                               sizeof(_target.packet_data.origNode.seqnum), false);
+    /* cppcheck: suppress false positive on non-trivially initialized arrays.
+     *           this is a known bug: http://trac.cppcheck.net/ticket/5497 */
+    /* cppcheck-suppress arrayIndexOutOfBounds */
+    rfc5444_writer_add_addrtlv(wr, origNode_addr, &_rreq_addrtlvs[RFC5444_MSGTLV_METRIC],
+                               &_target.packet_data.origNode.metric,
+                               sizeof(_target.packet_data.origNode.metric), false);
+}
+
+/**
+ * Callback to add addresses and address TLVs to a RFC5444 RREQ message
+ */
+static void
+_cb_rrep_addAddresses(struct rfc5444_writer *wr)
+{
+    AODV_DEBUG("%s()\n", __func__);
+
+    struct rfc5444_writer_address *origNode_addr, *targNode_addr;
+
+    uint16_t origNode_seqnum = _target.packet_data.origNode.seqnum;
+
+    uint16_t targNode_seqnum = seqnum_get();
+    seqnum_inc();
+
+    uint8_t targNode_hopCt = _target.packet_data.targNode.metric;
+
+    /* add origNode address (has no address tlv); is mandatory address */
+    origNode_addr = rfc5444_writer_add_address(wr, _rrep_message_content_provider.creator,
+                                               &_target.packet_data.origNode.addr, true);
+
+    /* add targNode address (has no address tlv); is mandatory address */
+    targNode_addr = rfc5444_writer_add_address(wr, _rrep_message_content_provider.creator,
+                                               &_target.packet_data.targNode.addr, true);
+
+    /* add OrigNode and TargNode SeqNum TLVs */
+    /* TODO: allow_dup true or false? */
+    rfc5444_writer_add_addrtlv(wr, origNode_addr, &_rrep_addrtlvs[RFC5444_MSGTLV_ORIGSEQNUM],
+                               &origNode_seqnum, sizeof(origNode_seqnum), false);
+    rfc5444_writer_add_addrtlv(wr, targNode_addr, &_rrep_addrtlvs[RFC5444_MSGTLV_TARGSEQNUM],
+                               &targNode_seqnum, sizeof(targNode_seqnum), false);
+
+    /* Add Metric TLV to targNode Address */
+    rfc5444_writer_add_addrtlv(wr, targNode_addr, &_rrep_addrtlvs[RFC5444_MSGTLV_METRIC],
+                               &targNode_hopCt, sizeof(targNode_hopCt), false);
+}
+
+/**
+ * Callback to add addresses and address TLVs to a RFC5444 RERR message
+ */
+static void
+_cb_rerr_addAddresses(struct rfc5444_writer *wr)
+{
+    AODV_DEBUG("%s()\n", __func__);
+
+    for (unsigned i = 0; i < _num_unreachable_nodes; i++) {
+        /* add unreachableNode addresses (has no address tlv); is mandatory address */
+        struct rfc5444_writer_address *unreachableNode_addr = rfc5444_writer_add_address(
+                                    wr, _rerr_message_content_provider.creator,
+                                    &_unreachable_nodes[i].addr, true);
+
+        /* add SeqNum TLV to unreachableNode */
+        /* TODO: allow_dup true or false? */
+        /* cppcheck: suppress false positive on non-trivially initialized arrays.
+         *           this is a known bug: http://trac.cppcheck.net/ticket/5497 */
+        /* cppcheck-suppress arrayIndexOutOfBounds */
+        rfc5444_writer_add_addrtlv(wr, unreachableNode_addr,
+                                   &_rerr_addrtlvs[RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM],
+                                   &_unreachable_nodes[i].seqnum,
+                                   sizeof(_unreachable_nodes[i].seqnum), false);
+    }
+}
+
+void aodv_packet_writer_init(write_packet_func_ptr ptr)
+{
+    AODV_DEBUG("%s()\n", __func__);
+
+    mutex_init(&writer_mutex);
+
+    /* define interface for generating rfc5444 packets */
+    _target.interface.packet_buffer = _packet_buffer;
+    _target.interface.packet_size = sizeof(_packet_buffer);
+
+    /* set function to send binary packet content */
+    _target.interface.sendPacket = ptr;
+
+    /* define the rfc5444 writer */
+    writer.msg_buffer = _msg_buffer;
+    writer.msg_size = sizeof(_msg_buffer);
+    writer.addrtlv_buffer = _msg_addrtlvs;
+    writer.addrtlv_size = sizeof(_msg_addrtlvs);
+
+    /* initialize writer */
+    rfc5444_writer_init(&writer);
+
+    /* register a target (for sending messages to) in writer */
+    rfc5444_writer_register_target(&writer, &_target.interface);
+
+    /* register a message content providers for RREQ and RREP */
+    rfc5444_writer_register_msgcontentprovider(&writer, &_rreq_message_content_provider,
+                                               _rreq_addrtlvs, ARRAYSIZE(_rreq_addrtlvs));
+    rfc5444_writer_register_msgcontentprovider(&writer, &_rrep_message_content_provider,
+                                               _rrep_addrtlvs, ARRAYSIZE(_rrep_addrtlvs));
+    rfc5444_writer_register_msgcontentprovider(&writer, &_rerr_message_content_provider,
+                                               _rerr_addrtlvs, ARRAYSIZE(_rerr_addrtlvs));
+
+    /* register rreq and rrep messages with 16 byte (ipv6) addresses.
+     * AddPacketHeader & addMessageHeader callbacks are triggered here. */
+    _rreq_msg = rfc5444_writer_register_message(&writer, RFC5444_MSGTYPE_RREQ,
+                                                false, RFC5444_MAX_ADDRLEN);
+    _rrep_msg = rfc5444_writer_register_message(&writer, RFC5444_MSGTYPE_RREP,
+                                                false, RFC5444_MAX_ADDRLEN);
+    _rerr_msg = rfc5444_writer_register_message(&writer, RFC5444_MSGTYPE_RERR,
+                                                false, RFC5444_MAX_ADDRLEN);
+
+    _rreq_msg->addMessageHeader = _cb_addMessageHeader;
+    _rrep_msg->addMessageHeader = _cb_addMessageHeader;
+    _rerr_msg->addMessageHeader = _cb_addMessageHeader;
+}
+
+/**
+ * Send a RREQ. DO NOT use this function to dispatch packets from anything else
+ * than the sender_thread. To send RREQs, use aodv_send_rreq().
+ * @param packet_data parameters of the RREQ
+ * @param next_hop Address the RREP is sent to
+ */
+void aodv_packet_writer_send_rreq(struct aodvv2_packet_data *packet_data, struct netaddr *next_hop)
+{
+    AODV_DEBUG("%s()\n", __func__);
+
+    if ((packet_data == NULL) || (next_hop == NULL)) {
+        return;
+    }
+
+    /* Make sure no other thread is using the writer right now */
+    mutex_lock(&writer_mutex);
+    memcpy(&_target.packet_data, packet_data, sizeof(struct aodvv2_packet_data));
+    _target.type = RFC5444_MSGTYPE_RREQ;
+    _target.packet_data.hoplimit = packet_data->hoplimit;
+
+    /* set address to which the write_packet callback should send our RREQ */
+    memcpy(&_target.target_addr, next_hop, sizeof (struct netaddr));
+
+    rfc5444_writer_create_message_alltarget(&writer, RFC5444_MSGTYPE_RREQ);
+    rfc5444_writer_flush(&writer, &_target.interface, false);
+    mutex_unlock(&writer_mutex);
+}
+
+
+/**
+ * Send a RREP. DO NOT use this function to dispatch packets from anything else
+ * than the sender_thread. To send RREPs, use aodv_send_rrep().
+ * @param packet_data parameters of the RREP
+ * @param next_hop Address the RREP is sent to
+ */
+void aodv_packet_writer_send_rrep(struct aodvv2_packet_data *packet_data, struct netaddr *next_hop)
+{
+    AODV_DEBUG("%s()\n", __func__);
+
+    if ((packet_data == NULL) || (next_hop == NULL)) {
+        return;
+    }
+
+    /* Make sure no other thread is using the writer right now */
+    mutex_lock(&writer_mutex);
+    memcpy(&_target.packet_data, packet_data, sizeof(struct aodvv2_packet_data));
+    _target.type = RFC5444_MSGTYPE_RREP;
+    _target.packet_data.hoplimit = AODVV2_MAX_HOPCOUNT;
+
+    /* set address to which the write_packet callback should send our RREQ */
+    memcpy(&_target.target_addr, next_hop, sizeof (struct netaddr));
+
+    rfc5444_writer_create_message_alltarget(&writer, RFC5444_MSGTYPE_RREP);
+    rfc5444_writer_flush(&writer, &_target.interface, false);
+    mutex_unlock(&writer_mutex);
+}
+
+/**
+ * Send a RERR. DO NOT use this function to dispatch packets from anything else
+ * than the sender_thread. To send RERRs, use aodv_send_rerr().
+ * @param unreachable_nodes[] array containing all newly unreachable nodes. each
+ *                            in a struct unreachable_node
+ * @param len                 length of unreachable_nodes[]
+ * @param hoplimit            the message's hop limit
+ * @param next_hop            Address the RREP is sent to
+ */
+void aodv_packet_writer_send_rerr(struct unreachable_node unreachable_nodes[], size_t len,
+                      int hoplimit, struct netaddr *next_hop)
+{
+    AODV_DEBUG("%s()\n", __func__);
+
+    if ((unreachable_nodes == NULL) || (next_hop == NULL)) {
+        return;
+    }
+
+    mutex_lock(&writer_mutex);
+    _target.packet_data.hoplimit = hoplimit;
+    _target.type = RFC5444_MSGTYPE_RERR;
+    _unreachable_nodes = unreachable_nodes;
+    _num_unreachable_nodes = len;
+
+    /* set address to which the write_packet callback should send our RREQ */
+    memcpy(&_target.target_addr, next_hop, sizeof (struct netaddr));
+
+    rfc5444_writer_create_message_alltarget(&writer, RFC5444_MSGTYPE_RERR);
+    rfc5444_writer_flush(&writer, &_target.interface, false);
+    mutex_unlock(&writer_mutex);
+}
+
+void aodv_packet_writer_cleanup(void)
+{
+    AODV_DEBUG("%s()\n", __func__);
+    rfc5444_writer_cleanup(&writer);
+}
diff --git a/sys/net/routing/aodvv2/writer.h b/sys/net/routing/aodvv2/writer.h
new file mode 100644
index 0000000000..f3ace00c2c
--- /dev/null
+++ b/sys/net/routing/aodvv2/writer.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 Freie Universität Berlin
+ * Copyright (C) 2014 Lotte Steenbrink <lotte.steenbrink@fu-berlin.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     aodvv2
+ * @{
+ *
+ * @file        writer.h
+ * @brief       writer to create RFC5444 aodvv2 messages
+ *
+ * @author      Lotte Steenbrink <lotte.steenbrink@fu-berlin.de>
+ */
+
+#ifndef AODVV2_WRITER_H_
+#define AODVV2_WRITER_H_
+
+#include "common/netaddr.h"
+#include "rfc5444/rfc5444_writer.h"
+#include "mutex.h"
+
+#include "constants.h"
+#include "seqnum.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Wrapper for the rfc5444_writer_target that the _write_packet() callback receives.
+ *          _write_packet() needs to know the type, payload and target address
+ *          of the RFC5444 message to be sent as well, but the oonf api does not
+ *          offer this feature. Having this wrapper enables the use of the
+ *          container_of macro to fetch this information.
+ *          It is hacky, but it does the trick.
+ */
+struct writer_target
+{
+    struct rfc5444_writer_target interface;     /**< Interface for generating rfc5444 packets */
+    struct netaddr target_addr;                 /**< Address to which the packet should be sent */
+    struct aodvv2_packet_data packet_data;      /**< Payload of the AODVv2 Message */
+    int type;                                   /**< Type of the AODVv2 Message (i.e. rfc5444_msg_type) */
+};
+
+/**
+ * @brief   oonf api voodo. Pointer to a callback function which is passed to
+ *          writer_init() and called when the packet is ready to send.
+ */
+typedef void (*write_packet_func_ptr)(
+    struct rfc5444_writer *wr, struct rfc5444_writer_target *iface,
+    void *buffer, size_t length);
+
+/**
+ * @brief   Initialize RFC5444 writer
+ * @param ptr   pointer to "send_packet" callback
+ */
+void aodv_packet_writer_init(write_packet_func_ptr ptr);
+
+/**
+ * @brief   Clean up after the RFC5444 writer
+ */
+void aodv_packet_writer_cleanup(void);
+
+/**
+ * @brief   Send a RREQ. DO NOT use this function to dispatch packets from anything else
+ *          than the sender_thread. To send RREQs, use aodv_send_rreq().
+ * @param packet_data      parameters of the RREQ
+ * @param next_hop Address the RREP is sent to
+ */
+void aodv_packet_writer_send_rreq(struct aodvv2_packet_data *packet_data, struct netaddr *next_hop);
+
+/**
+ * @brief   Send a RREP. DO NOT use this function to dispatch packets from anything else
+ *          than the sender_thread. To send RREPs, use aodv_send_rrep().
+ * @param packet_data parameters of the RREP
+ * @param next_hop Address the RREP is sent to
+ */
+void aodv_packet_writer_send_rrep(struct aodvv2_packet_data *packet_data, struct netaddr *next_hop);
+
+/**
+ * @brief   Send a RERR. DO NOT use this function to dispatch packets from anything else
+ *          than the sender_thread. To send RERRs, use aodv_send_rerr().
+ * @param unreachable_nodes[] array containing all newly unreachable nodes. each
+ *                            in a struct unreachable_node
+ * @param len                 length of unreachable_nodes[]
+ * @param hoplimit            the message's hop limit
+ * @param next_hop            Address the RREP is sent to
+ */
+void aodv_packet_writer_send_rerr(struct unreachable_node unreachable_nodes[], size_t len,
+                      int hoplimit, struct netaddr *next_hop);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* AODVV2_WRITER_H_ */
-- 
GitLab