diff --git a/Makefile.dep b/Makefile.dep
index ed6742868f1bb73c2ca2e3f115fc8f0042b4c106..63a105a39a9c4c4070e860546be07015b2369e6c 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 2a8f2210b87cfb77652b2bbfceb628a856aae9ed..6e12c3770e9fd20b2b1425bcb9e71c0f0f85e81c 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 0000000000000000000000000000000000000000..86b6b0329063d6740ecc686d75f79a1a959df86f
--- /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 0000000000000000000000000000000000000000..47de03dd6321c7120a0c77fcc1eb36f5b4e239b3
--- /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 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2
--- /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 0000000000000000000000000000000000000000..47f216333ef6a50cea5b84a4fc15aca803005f65
--- /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 0000000000000000000000000000000000000000..f210b7e22e573b476989dd3ec3afaf18e98fca23
--- /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 0000000000000000000000000000000000000000..75d681f3692367df83a3abb1ab618eb0a48e5f8c
--- /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 0000000000000000000000000000000000000000..3567ccfce995a0f70124630d1a83884f379bf49f
--- /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 0000000000000000000000000000000000000000..1333a807fb729f7e4277afef4e710e182d6016b4
--- /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 0000000000000000000000000000000000000000..07fe13384393a3b47f8d1bd44b6d47115c340a19
--- /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 0000000000000000000000000000000000000000..e7a7316ef2fc8cdf287c993fbd8898d9d8149b5e
--- /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 0000000000000000000000000000000000000000..5e1e533aaac4bceab9682c8445c09a7514c9e091
--- /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 0000000000000000000000000000000000000000..d6da9ae314af113dd074a271e7a213b009a78dcd
--- /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 0000000000000000000000000000000000000000..0a11a44b6c05a2e7a47d08e70c60c7e62c2afc6e
--- /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 0000000000000000000000000000000000000000..de48d134427decc411b83388dd43eeedc4f3d8eb
--- /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 0000000000000000000000000000000000000000..8baee0e1f98d871339bfb108bdd870abef3e4632
--- /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 0000000000000000000000000000000000000000..79acc8e024af23997b54ab9cbb46ae3b08dc376e
--- /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 0000000000000000000000000000000000000000..f3ace00c2c9b426a193055b24b1b2cb9e16625b1
--- /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_ */