diff --git a/Makefile.pseudomodules b/Makefile.pseudomodules
index 038de122c2ee8a7f53dac4fcd93198c8310520d6..7ba7ff37e12c4321e3363f9e0a57ce2526963f5b 100644
--- a/Makefile.pseudomodules
+++ b/Makefile.pseudomodules
@@ -6,6 +6,7 @@ PSEUDOMODULES += gnrc_netif_default
 PSEUDOMODULES += gnrc_ipv6_default
 PSEUDOMODULES += gnrc_ipv6_router
 PSEUDOMODULES += gnrc_ipv6_router_default
+PSEUDOMODULES += gnrc_neterr
 PSEUDOMODULES += gnrc_sixlowpan_default
 PSEUDOMODULES += gnrc_sixlowpan_border_router_default
 PSEUDOMODULES += gnrc_sixlowpan_nd_border_router
diff --git a/sys/include/net/gnrc/neterr.h b/sys/include/net/gnrc/neterr.h
new file mode 100644
index 0000000000000000000000000000000000000000..19d1c41ceb24d4a2a13bba707eeba651bc93e1fd
--- /dev/null
+++ b/sys/include/net/gnrc/neterr.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 Martine Lenders <mlenders@inf.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    net_gnrc_neterr Error reporting
+ * @ingroup     net
+ * @brief       Allows for asynchronous error reporting in the network stack.
+ * @{
+ *
+ * @file
+ * @brief   Error reporting definitions.
+ *
+ * @author  Martine Lenders <mlenders@inf.fu-berlin.de>
+ */
+#ifndef GNRC_NETERR_H_
+#define GNRC_NETERR_H_
+
+#include <errno.h>
+#include <stdint.h>
+
+#include "kernel_types.h"
+#include "msg.h"
+#include "net/gnrc/pkt.h"
+#include "thread.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   @ref core_msg type for reporting an error.
+ */
+#define GNRC_NETERR_MSG_TYPE        (0x0206)
+
+/**
+ * @brief   Error code to signalise success (no error occured) to an gnrc_neterr subscriber.
+ */
+#define GNRC_NETERR_SUCCESS         (0)
+
+/**
+ * @brief   Reports an error to all subscribers of errors to @p pkt.
+ *
+ * @param[in] pkt   Packet snip to report on.
+ * @param[in] err   The error code for the packet.
+ */
+#ifdef MODULE_GNRC_NETERR
+static inline void gnrc_neterr_report(gnrc_pktsnip_t *pkt, uint32_t err)
+{
+    if (pkt->err_sub != KERNEL_PID_UNDEF) {
+        msg_t msg;
+
+        msg.type = GNRC_NETERR_MSG_TYPE;
+        msg.content.value = err;
+
+        msg_send(&msg, pkt->err_sub);
+    }
+}
+#else
+#define gnrc_neterr_report(pkt, err)  (void)pkt; (void)err
+#endif
+
+/**
+ * @brief   Registers the current thread for errors on a @ref gnrc_pktsnip_t.
+ *
+ * @param[in] pkt   Packet snip to register for errors.
+ *
+ * @return  0, on success.
+ * @return  EALREADY, if there already someone registered to errors on @p pkt.
+ */
+#ifdef MODULE_GNRC_NETERR
+static inline int gnrc_neterr_reg(gnrc_pktsnip_t *pkt)
+{
+    if (pkt->err_sub != KERNEL_PID_UNDEF) {
+        return EALREADY;
+    }
+    pkt->err_sub = sched_active_pid;
+    return 0;
+}
+#else
+#define gnrc_neterr_reg(pkt)  (0)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GNRC_NETERR_H_ */
+/** @} */
diff --git a/sys/include/net/gnrc/pkt.h b/sys/include/net/gnrc/pkt.h
index 6d2475ad42359b4c7c44472977ba470360527ed6..7d4ab80dd23ddfc0f20cff215403ff1c9de71a3b 100644
--- a/sys/include/net/gnrc/pkt.h
+++ b/sys/include/net/gnrc/pkt.h
@@ -25,6 +25,7 @@
 #include <inttypes.h>
 #include <stdlib.h>
 
+#include "kernel_types.h"
 #include "net/gnrc/nettype.h"
 
 #ifdef __cplusplus
@@ -110,6 +111,10 @@ typedef struct gnrc_pktsnip {
     void *data;                     /**< pointer to the data of the snip */
     size_t size;                    /**< the length of the snip in byte */
     gnrc_nettype_t type;            /**< protocol of the packet snip */
+#ifdef MODULE_GNRC_NETERR
+    kernel_pid_t err_sub;           /**< subscriber to errors related to this
+                                     *   packet snip */
+#endif
 } gnrc_pktsnip_t;
 
 /**
diff --git a/sys/include/net/gnrc/pktbuf.h b/sys/include/net/gnrc/pktbuf.h
index 6234e1c0e721c9b6239ff5feb346e36d4c771314..e1ddfc7815d4e777bfbd85d22b16913df5457844 100644
--- a/sys/include/net/gnrc/pktbuf.h
+++ b/sys/include/net/gnrc/pktbuf.h
@@ -37,6 +37,7 @@
 #include "atomic.h"
 #include "cpu_conf.h"
 #include "net/gnrc/pkt.h"
+#include "net/gnrc/neterr.h"
 #include "net/gnrc/nettype.h"
 #include "utlist.h"
 
@@ -141,13 +142,26 @@ void gnrc_pktbuf_hold(gnrc_pktsnip_t *pkt, unsigned int num);
 
 /**
  * @brief   Decreases gnrc_pktsnip_t::users of @p pkt atomically and removes it if it
- *          reaches 0.
+ *          reaches 0 and reports a possible error through an error code, if
+ *          @ref net_gnrc_neterr is included.
  *
  * @pre All snips of @p pkt must be in the packet buffer.
  *
  * @param[in] pkt   A packet.
+ * @param[in] err   An error code.
  */
-void gnrc_pktbuf_release(gnrc_pktsnip_t *pkt);
+void gnrc_pktbuf_release_error(gnrc_pktsnip_t *pkt, uint32_t err);
+
+/**
+ * @brief   Decreases gnrc_pktsnip_t::users of @p pkt atomically and removes it if it
+ *          reaches 0 and reports @ref GNRC_NETERR_SUCCESS.
+ *
+ * @param[in] pkt   A packet.
+ */
+static inline void gnrc_pktbuf_release(gnrc_pktsnip_t *pkt)
+{
+    gnrc_pktbuf_release_error(pkt, GNRC_NETERR_SUCCESS);
+}
 
 /**
  * @brief   Must be called once before there is a write operation in a thread.
@@ -189,7 +203,7 @@ gnrc_pktsnip_t *gnrc_pktbuf_get_iovec(gnrc_pktsnip_t *pkt, size_t *len);
  * @return  The new reference to @p pkt.
  */
 static inline gnrc_pktsnip_t *gnrc_pktbuf_remove_snip(gnrc_pktsnip_t *pkt,
-        gnrc_pktsnip_t *snip)
+                                                      gnrc_pktsnip_t *snip)
 {
     LL_DELETE(pkt, snip);
     snip->next = NULL;
diff --git a/sys/net/gnrc/pktbuf_static/gnrc_pktbuf_static.c b/sys/net/gnrc/pktbuf_static/gnrc_pktbuf_static.c
index edc2858157651ae46954772e47c8c0852b26e49b..76887217e895cd492abf7be0bada817c856e3568 100644
--- a/sys/net/gnrc/pktbuf_static/gnrc_pktbuf_static.c
+++ b/sys/net/gnrc/pktbuf_static/gnrc_pktbuf_static.c
@@ -59,9 +59,21 @@ static inline bool _pktbuf_contains(void *ptr)
 /* fits size to byte alignment */
 static inline size_t _align(size_t size)
 {
-    return ((size + _ALIGNMENT_MASK) & ~(_ALIGNMENT_MASK));
+    return (size + _ALIGNMENT_MASK) & ~(_ALIGNMENT_MASK);
 }
 
+static inline void _set_pktsnip(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t *next,
+                                void *data, size_t size, gnrc_nettype_t type)
+{
+    pkt->next = next;
+    pkt->data = data;
+    pkt->size = size;
+    pkt->type = type;
+    pkt->users = 1;
+#ifdef MODULE_GNRC_NETERR
+    pkt->err_sub = KERNEL_PID_UNDEF;
+#endif
+}
 
 void gnrc_pktbuf_init(void)
 {
@@ -76,6 +88,7 @@ gnrc_pktsnip_t *gnrc_pktbuf_add(gnrc_pktsnip_t *next, void *data, size_t size,
                                 gnrc_nettype_t type)
 {
     gnrc_pktsnip_t *pkt;
+
     if ((size == 0) || (size > GNRC_PKTBUF_SIZE)) {
         DEBUG("pktbuf: size (%u) == 0 || size == GNRC_PKTBUF_SIZE (%u)\n",
               (unsigned)size, GNRC_PKTBUF_SIZE);
@@ -93,6 +106,8 @@ gnrc_pktsnip_t *gnrc_pktbuf_mark(gnrc_pktsnip_t *pkt, size_t size, gnrc_nettype_
     /* size required for chunk */
     size_t required_new_size = (size < sizeof(_unused_t)) ?
                                _align(sizeof(_unused_t)) : _align(size);
+    void *new_data_marked;
+
     mutex_lock(&_mutex);
     if ((size == 0) || (pkt == NULL) || (size > pkt->size) || (pkt->data == NULL)) {
         DEBUG("pktbuf: size == 0 (was %u) or pkt == NULL (was %p) or "
@@ -115,7 +130,7 @@ gnrc_pktsnip_t *gnrc_pktbuf_mark(gnrc_pktsnip_t *pkt, size_t size, gnrc_nettype_
     }
     /* would not fit unused marker => move data around */
     if ((size < required_new_size) || ((pkt->size - size) < sizeof(_unused_t))) {
-        void *new_data_marked, *new_data_rest;
+        void *new_data_rest;
         new_data_marked = _pktbuf_alloc(size);
         if (new_data_marked == NULL) {
             DEBUG("pktbuf: could not reallocate marked section.\n");
@@ -138,14 +153,11 @@ gnrc_pktsnip_t *gnrc_pktbuf_mark(gnrc_pktsnip_t *pkt, size_t size, gnrc_nettype_
         pkt->data = new_data_rest;
     }
     else {
-        marked_snip->data = pkt->data;
+        new_data_marked = pkt->data;
         pkt->data = ((uint8_t *)pkt->data) + size;
     }
     pkt->size -= size;
-    marked_snip->next = pkt->next;
-    marked_snip->size = size;
-    marked_snip->type = type;
-    marked_snip->users = 1;
+    _set_pktsnip(marked_snip, pkt->next, new_data_marked, size, type);
     pkt->next = marked_snip;
     mutex_unlock(&_mutex);
     return marked_snip;
@@ -155,6 +167,7 @@ int gnrc_pktbuf_realloc_data(gnrc_pktsnip_t *pkt, size_t size)
 {
     size_t aligned_size = (size < sizeof(_unused_t)) ?
                           _align(sizeof(_unused_t)) : _align(size);
+
     mutex_lock(&_mutex);
     assert((pkt != NULL) && (pkt->data != NULL) && _pktbuf_contains(pkt->data));
     if (size == 0) {
@@ -199,7 +212,7 @@ void gnrc_pktbuf_hold(gnrc_pktsnip_t *pkt, unsigned int num)
     mutex_unlock(&_mutex);
 }
 
-void gnrc_pktbuf_release(gnrc_pktsnip_t *pkt)
+void gnrc_pktbuf_release_error(gnrc_pktsnip_t *pkt, uint32_t err)
 {
     mutex_lock(&_mutex);
     while (pkt) {
@@ -214,6 +227,8 @@ void gnrc_pktbuf_release(gnrc_pktsnip_t *pkt)
         else {
             pkt->users--;
         }
+        DEBUG("pktbuf: report status code %" PRIu32 "\n", err);
+        gnrc_neterr_report(pkt, err);
         pkt = tmp;
     }
     mutex_unlock(&_mutex);
@@ -372,6 +387,7 @@ static gnrc_pktsnip_t *_create_snip(gnrc_pktsnip_t *next, void *data, size_t siz
 {
     gnrc_pktsnip_t *pkt = _pktbuf_alloc(sizeof(gnrc_pktsnip_t));
     void *_data;
+
     if (pkt == NULL) {
         DEBUG("pktbuf: error allocating new packet snip\n");
         return NULL;
@@ -382,11 +398,7 @@ static gnrc_pktsnip_t *_create_snip(gnrc_pktsnip_t *next, void *data, size_t siz
         _pktbuf_free(pkt, sizeof(gnrc_pktsnip_t));
         return NULL;
     }
-    pkt->next = next;
-    pkt->size = size;
-    pkt->data = _data;
-    pkt->type = type;
-    pkt->users = 1;
+    _set_pktsnip(pkt, next, _data, size, type);
     if (data != NULL) {
         memcpy(_data, data, size);
     }
@@ -396,6 +408,7 @@ static gnrc_pktsnip_t *_create_snip(gnrc_pktsnip_t *next, void *data, size_t siz
 static void *_pktbuf_alloc(size_t size)
 {
     _unused_t *prev = NULL, *ptr = _first_unused;
+
     size = (size < sizeof(_unused_t)) ? _align(sizeof(_unused_t)) : _align(size);
     while (ptr && (size > ptr->size)) {
         prev = ptr;
@@ -444,6 +457,7 @@ static inline _unused_t *_merge(_unused_t *a, _unused_t *b)
 static void _pktbuf_free(void *data, size_t size)
 {
     _unused_t *new = (_unused_t *)data, *prev = NULL, *ptr = _first_unused;
+
     if (!_pktbuf_contains(data)) {
         return;
     }