diff --git a/examples/gnrc_networking/Makefile b/examples/gnrc_networking/Makefile
index 4d361f8250fcb64b0ba40c8b3efca92be92daffc..7aac1aee1282c914dfe5e460be45a466d267c051 100644
--- a/examples/gnrc_networking/Makefile
+++ b/examples/gnrc_networking/Makefile
@@ -20,6 +20,8 @@ BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-mega2560 arduino-uno \
 # NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
 USEMODULE += gnrc_netdev_default
 USEMODULE += auto_init_gnrc_netif
+# Activate ICMPv6 error messages
+USEMODULE += gnrc_icmpv6_error
 # Specify the mandatory networking modules for IPv6 and UDP
 USEMODULE += gnrc_ipv6_router_default
 USEMODULE += gnrc_udp
diff --git a/sys/include/net/gnrc/icmpv6/error.h b/sys/include/net/gnrc/icmpv6/error.h
index 20d0343196f66b31ff8c648925b78ec9f572d949..7bd527e13162a71a217b8ecbc01ccf86de2c10fa 100644
--- a/sys/include/net/gnrc/icmpv6/error.h
+++ b/sys/include/net/gnrc/icmpv6/error.h
@@ -33,133 +33,63 @@
 extern "C" {
 #endif
 
-/**
- * @brief   Builds an ICMPv6 destination unreachable message for sending.
- *
- * @param[in] code      The code for the message @see net/icmpv6.h.
- * @param[in] orig_pkt  The invoking packet.
- *
- * @return  The destination unreachable message on success.
- * @return  NULL, on failure.
- */
-gnrc_pktsnip_t *gnrc_icmpv6_error_dst_unr_build(uint8_t code, gnrc_pktsnip_t *orig_pkt);
-
-/**
- * @brief   Builds an ICMPv6 packet too big message for sending.
- *
- * @param[in] mtu       The maximum transission unit of the next-hop link.
- * @param[in] orig_pkt  The invoking packet.
- *
- * @return  The packet too big message on success.
- * @return  NULL, on failure.
- */
-gnrc_pktsnip_t *gnrc_icmpv6_error_pkt_too_big_build(uint32_t mtu, gnrc_pktsnip_t *orig_pkt);
-
-/**
- * @brief   Builds an ICMPv6 time exceeded message for sending.
- *
- * @param[in] code      The code for the message @see net/icmpv6.h.
- * @param[in] orig_pkt  The invoking packet.
- *
- * @return  The time exceeded message on success.
- * @return  NULL, on failure.
- */
-gnrc_pktsnip_t *gnrc_icmpv6_error_time_exc_build(uint8_t code, gnrc_pktsnip_t *orig_pkt);
-
-/**
- * @brief   Builds an ICMPv6 parameter problem message for sending.
- *
- * @param[in] code      The code for the message @see net/icmpv6.h.
- * @param[in] ptr       Pointer to the errorneous octet in @p orig_pkt.
- * @param[in] orig_pkt  The invoking packet.
- *
- * @return  The parameter problem message on success.
- * @return  NULL, on failure.
- */
-gnrc_pktsnip_t *gnrc_icmpv6_error_param_prob_build(uint8_t code, void *ptr,
-                                                   gnrc_pktsnip_t *orig_pkt);
-
+#if defined(MODULE_GNRC_ICMPV6_ERROR) || defined(DOXYGEN)
 /**
  * @brief   Sends an ICMPv6 destination unreachable message for sending.
  *
- * @param[in] code      The code for the message @see net/icmpv6.h.
+ * @pre @p orig_pkt contains a packet snip of type @ref GNRC_NETTYPE_IPV6
+ *
+ * @param[in] code      The [code for the message](@ref net_icmpv6_error_dst_unr_codes).
  * @param[in] orig_pkt  The invoking packet.
  */
-static inline void gnrc_icmpv6_error_dst_unr_send(uint8_t code, gnrc_pktsnip_t *orig_pkt)
-{
-    gnrc_pktsnip_t *pkt = gnrc_icmpv6_error_dst_unr_build(code, orig_pkt);
-
-    if (pkt != NULL) {
-        gnrc_netapi_send(gnrc_ipv6_pid, pkt);
-    }
-#ifdef MODULE_GNRC_PKTBUF
-    gnrc_pktbuf_release_error(orig_pkt, EHOSTUNREACH);
-#else
-    (void)orig_pkt;
-#endif
-}
+void gnrc_icmpv6_error_dst_unr_send(uint8_t code, const gnrc_pktsnip_t *orig_pkt);
 
 /**
  * @brief   Sends an ICMPv6 packet too big message for sending.
  *
+ * @pre @p orig_pkt contains a packet snip of type @ref GNRC_NETTYPE_IPV6
+ *
  * @param[in] mtu       The maximum transission unit of the next-hop link.
  * @param[in] orig_pkt  The invoking packet.
  */
-static inline void gnrc_icmpv6_error_pkt_too_big_send(uint32_t mtu, gnrc_pktsnip_t *orig_pkt)
-{
-    gnrc_pktsnip_t *pkt = gnrc_icmpv6_error_pkt_too_big_build(mtu, orig_pkt);
-
-    if (pkt != NULL) {
-        gnrc_netapi_send(gnrc_ipv6_pid, pkt);
-    }
-#ifdef MODULE_GNRC_PKTBUF
-    gnrc_pktbuf_release_error(orig_pkt, EMSGSIZE);
-#else
-    (void)orig_pkt;
-#endif
-}
+void gnrc_icmpv6_error_pkt_too_big_send(uint32_t mtu,
+                                        const gnrc_pktsnip_t *orig_pkt);
 
 /**
  * @brief   Sends an ICMPv6 time exceeded message for sending.
  *
- * @param[in] code      The code for the message @see net/icmpv6.h.
+ * @pre @p orig_pkt contains a packet snip of type @ref GNRC_NETTYPE_IPV6
+ *
+ * @param[in] code      The [code for the message](@ref net_icmpv6_error_time_exc_codes).
  * @param[in] orig_pkt  The invoking packet.
  */
-static inline void gnrc_icmpv6_error_time_exc_send(uint8_t code, gnrc_pktsnip_t *orig_pkt)
-{
-    gnrc_pktsnip_t *pkt = gnrc_icmpv6_error_time_exc_build(code, orig_pkt);
-
-    if (pkt != NULL) {
-        gnrc_netapi_send(gnrc_ipv6_pid, pkt);
-    }
-#ifdef MODULE_GNRC_PKTBUF
-    gnrc_pktbuf_release_error(orig_pkt, ETIMEDOUT);
-#else
-    (void)orig_pkt;
-#endif
-}
+void gnrc_icmpv6_error_time_exc_send(uint8_t code,
+                                     const gnrc_pktsnip_t *orig_pkt);
 
 /**
  * @brief   Sends an ICMPv6 parameter problem message for sending.
  *
- * @param[in] code      The code for the message @see net/icmpv6.h.
+ * @pre @p orig_pkt is in receive order.
+ * @pre @p orig_pkt contains a packet snip of type @ref GNRC_NETTYPE_IPV6
+ *
+ *
+ * @param[in] code      The [code for the message](@ref net_icmpv6_error_param_prob_codes).
  * @param[in] ptr       Pointer to the errorneous octet in @p orig_pkt.
  * @param[in] orig_pkt  The invoking packet.
  */
-static inline void gnrc_icmpv6_error_param_prob_send(uint8_t code, void *ptr,
-                                                     gnrc_pktsnip_t *orig_pkt)
-{
-    gnrc_pktsnip_t *pkt = gnrc_icmpv6_error_param_prob_build(code, ptr, orig_pkt);
-
-    if (pkt != NULL) {
-        gnrc_netapi_send(gnrc_ipv6_pid, pkt);
-    }
-#ifdef MODULE_GNRC_PKTBUF
-    gnrc_pktbuf_release_error(orig_pkt, EINVAL);
-#else
-    (void)orig_pkt;
-#endif
-}
+void gnrc_icmpv6_error_param_prob_send(uint8_t code, void *ptr,
+                                       const gnrc_pktsnip_t *orig_pkt);
+#else   /* defined(MODULE_GNRC_ICMPV6_ERROR) || defined(DOXYGEN) */
+/* NOPs to make the usage code more readable */
+#define gnrc_icmpv6_error_dst_unr_send(code, orig_pkt) \
+        (void)code; (void)orig_pkt
+#define gnrc_icmpv6_error_pkt_too_big_send(mtu, orig_pkt) \
+        (void)mtu; (void)orig_pkt
+#define gnrc_icmpv6_error_time_exc_send(code, orig_pkt) \
+        (void)code; (void)orig_pkt
+#define gnrc_icmpv6_error_param_prob_send(code, ptr, orig_pkt) \
+        (void)code; (void)ptr, (void)orig_pkt
+#endif  /* defined(MODULE_GNRC_ICMPV6_ERROR) || defined(DOXYGEN) */
 
 #ifdef __cplusplus
 }
diff --git a/sys/include/net/icmpv6.h b/sys/include/net/icmpv6.h
index 3a9e606e16952ab9335b90aaf1cabc7f3036f468..29d0bec7e078c513d71b68961f006a07b52a8b59 100644
--- a/sys/include/net/icmpv6.h
+++ b/sys/include/net/icmpv6.h
@@ -75,6 +75,7 @@ extern "C" {
  * @{
  * @name Codes for destination unreachable messages
  *
+ * @anchor net_icmpv6_error_dst_unr_codes
  * @see <a href="https://tools.ietf.org/html/rfc4443#section-3.1">
  *          RFC 4443, section 3.1
  *      </a>
@@ -97,6 +98,7 @@ extern "C" {
  * @{
  * @name Codes for time exceeded messages
  *
+ * @anchor net_icmpv6_error_time_exc_codes
  * @see <a href="https://tools.ietf.org/html/rfc4443#section-3.3">
  *          RFC 4443, section 3.3
  *      </a>
@@ -111,6 +113,7 @@ extern "C" {
  * @{
  * @name Codes for parameter problem messages
  *
+ * @anchor net_icmpv6_error_param_prob_codes
  * @see <a href="https://tools.ietf.org/html/rfc4443#section-3.4">
  *          RFC 4443, section 3.4
  *      </a>
diff --git a/sys/net/gnrc/network_layer/icmpv6/error/gnrc_icmpv6_error.c b/sys/net/gnrc/network_layer/icmpv6/error/gnrc_icmpv6_error.c
index 4313414c45e583b2d92adf22f15f9fd6513e6682..ba1e028fb4943e3db695f70579b43999fdedaefe 100644
--- a/sys/net/gnrc/network_layer/icmpv6/error/gnrc_icmpv6_error.c
+++ b/sys/net/gnrc/network_layer/icmpv6/error/gnrc_icmpv6_error.c
@@ -12,59 +12,145 @@
  * @file
  */
 
+#include "net/ipv6.h"
+#include "net/gnrc/icmpv6.h"
+#include "net/gnrc/netif.h"
 #include "net/gnrc/pktbuf.h"
 
-#include "net/ipv6.h"
 #include "net/gnrc/icmpv6/error.h"
-#include "net/gnrc/icmpv6.h"
+
+#define ENABLE_DEBUG    (0)
+#include "debug.h"
 
 /* all error messages are basically the same size and format */
 #define ICMPV6_ERROR_SZ (sizeof(icmpv6_error_dst_unr_t))
 #define ICMPV6_ERROR_SET_VALUE(data, value) \
     ((icmpv6_error_pkt_too_big_t *)(data))->mtu = byteorder_htonl(value)
 
-/* TODO: generalize and centralize (see https://github.com/RIOT-OS/RIOT/pull/3184) */
 #define MIN(a, b)   ((a) < (b)) ? (a) : (b)
 
-static inline size_t _fit(gnrc_pktsnip_t *pkt)
+/**
+ * @brief   Get packet fit.
+ *
+ * Get's the minimum size for an ICMPv6 error message packet, based on the
+ * invoking packet's size and the interface the invoking packet came over.
+ *
+ * @param[in] orig_pkt  The invoking packet
+ *
+ * @return  The supposed size of the ICMPv6 error message.
+ */
+static size_t _fit(const gnrc_pktsnip_t *orig_pkt)
+{
+    /* discarding const qualifier is safe here */
+    gnrc_pktsnip_t *netif_hdr = gnrc_pktsnip_search_type(
+            (gnrc_pktsnip_t *)orig_pkt, GNRC_NETTYPE_NETIF
+        );
+    size_t pkt_len = gnrc_pkt_len(orig_pkt) + ICMPV6_ERROR_SZ +
+                     sizeof(ipv6_hdr_t);
+
+    if (netif_hdr) {
+        gnrc_netif_hdr_t *data = netif_hdr->data;
+        gnrc_netif_t *netif = gnrc_netif_get_by_pid(data->if_pid);
+
+        pkt_len -= netif_hdr->size;
+        DEBUG("gnrc_icmpv6_error: fitting to MTU of iface %u (%u)\n",
+              netif->pid, netif->ipv6.mtu);
+        return MIN(pkt_len, netif->ipv6.mtu - sizeof(ipv6_hdr_t));
+    }
+    else {
+        /* packet does not have a netif header (most likely because it did not
+         * came from remote) => just assume pkt_len as ideal */
+        DEBUG("gnrc_icmpv6_error: copying whole packet\n");
+        return pkt_len;
+    }
+}
+
+static inline bool _in_orig_pkt(const gnrc_pktsnip_t *orig_pkt)
 {
-    /* TODO: replace IPV6_MIN_MTU with known path MTU? */
-    return MIN((gnrc_pkt_len(pkt) + ICMPV6_ERROR_SZ), IPV6_MIN_MTU);
+    return (orig_pkt != NULL) && (orig_pkt->type != GNRC_NETTYPE_NETIF);
+}
+
+static size_t _copy_rcv_snip(gnrc_pktsnip_t *pkt,
+                             const gnrc_pktsnip_t *orig_snip)
+{
+    /* always skip ICMPv6 error header */
+    size_t offset = ICMPV6_ERROR_SZ;
+    const gnrc_pktsnip_t *ptr = orig_snip;
+
+    while (_in_orig_pkt(ptr->next)) {
+        offset += ptr->next->size;
+        ptr = ptr->next;
+    }
+
+    if (offset < pkt->size) {
+        uint8_t *data = pkt->data;
+
+        memcpy(data + offset, orig_snip->data,
+               MIN(pkt->size - offset, orig_snip->size));
+    }
+    return offset;
+}
+
+static inline bool _check_send_order(const gnrc_pktsnip_t *pkt)
+{
+    /* sent packets in IPv6 start either with netif header or
+     * with IPv6 header (but then the NETIF header doesn't follow) */
+    return (pkt->type == GNRC_NETTYPE_NETIF) ||
+           ((pkt->type == GNRC_NETTYPE_IPV6) &&
+            ((pkt->next == NULL) || (pkt->next->type != GNRC_NETTYPE_NETIF)));
 }
 
 /* Build a generic error message */
 static gnrc_pktsnip_t *_icmpv6_error_build(uint8_t type, uint8_t code,
-                                           gnrc_pktsnip_t *orig_pkt, uint32_t value)
+                                           const gnrc_pktsnip_t *orig_pkt,
+                                           uint32_t value)
 {
     gnrc_pktsnip_t *pkt = gnrc_icmpv6_build(NULL, type, code, _fit(orig_pkt));
 
-    /* copy as much of the originating packet into error message as fits the message's size */
+    /* copy as much of the originating packet into error message as fits the
+     * message's size */
     if (pkt != NULL) {
-        size_t offset = ICMPV6_ERROR_SZ;
-        uint8_t *data = pkt->data;
-        ICMPV6_ERROR_SET_VALUE(data, value);
-        while ((orig_pkt != NULL) && (offset < pkt->size)) {
-            memcpy(data + offset, orig_pkt->data,
-                   MIN(pkt->size - offset, orig_pkt->size));
-            offset += MIN(pkt->size - offset, orig_pkt->size);
-            orig_pkt = orig_pkt->next;
+        ICMPV6_ERROR_SET_VALUE(pkt->data, value);
+        if (_check_send_order(orig_pkt)) {
+            const gnrc_pktsnip_t *ptr = (orig_pkt->type == GNRC_NETTYPE_NETIF)
+                                      ? orig_pkt->next
+                                      : orig_pkt;
+            size_t offset = ICMPV6_ERROR_SZ;
+
+            while ((ptr != NULL) && (offset < pkt->size)) {
+                uint8_t *data = pkt->data;
+
+                memcpy(data + offset, ptr->data, MIN(pkt->size - offset,
+                                                     ptr->size));
+                offset += ptr->size;
+                ptr = ptr->next;
+            }
+        }
+        else {
+            while (_in_orig_pkt(orig_pkt)) {
+                _copy_rcv_snip(pkt, orig_pkt);
+                orig_pkt = orig_pkt->next;
+            }
         }
     }
 
     return pkt;
 }
 
-gnrc_pktsnip_t *gnrc_icmpv6_error_dst_unr_build(uint8_t code, gnrc_pktsnip_t *orig_pkt)
+static inline gnrc_pktsnip_t *_dst_unr_build(uint8_t code,
+                                             const gnrc_pktsnip_t *orig_pkt)
 {
     return _icmpv6_error_build(ICMPV6_DST_UNR, code, orig_pkt, 0);
 }
 
-gnrc_pktsnip_t *gnrc_icmpv6_error_pkt_too_big_build(uint32_t mtu, gnrc_pktsnip_t *orig_pkt)
+static inline gnrc_pktsnip_t *_pkt_too_big_build(uint32_t mtu,
+                                                 const gnrc_pktsnip_t *orig_pkt)
 {
     return _icmpv6_error_build(ICMPV6_PKT_TOO_BIG, 0, orig_pkt, mtu);
 }
 
-gnrc_pktsnip_t *gnrc_icmpv6_error_time_exc_build(uint8_t code, gnrc_pktsnip_t *orig_pkt)
+static inline gnrc_pktsnip_t *_time_exc_build(uint8_t code,
+                                              const gnrc_pktsnip_t *orig_pkt)
 {
     return _icmpv6_error_build(ICMPV6_TIME_EXC, code, orig_pkt, 0);
 }
@@ -74,8 +160,8 @@ static inline bool _in_range(uint8_t *ptr, uint8_t *start, size_t sz)
     return (ptr >= start) && (ptr < (start + sz));
 }
 
-gnrc_pktsnip_t *gnrc_icmpv6_error_param_prob_build(uint8_t code, void *ptr,
-                                                   gnrc_pktsnip_t *orig_pkt)
+static gnrc_pktsnip_t *_param_prob_build(uint8_t code, void *ptr,
+                                         const gnrc_pktsnip_t *orig_pkt)
 {
     gnrc_pktsnip_t *pkt = gnrc_icmpv6_build(NULL, ICMPV6_PARAM_PROB, code,
                                             _fit(orig_pkt));
@@ -83,39 +169,107 @@ gnrc_pktsnip_t *gnrc_icmpv6_error_param_prob_build(uint8_t code, void *ptr,
     /* copy as much of the originating packet into error message and
      * determine relative *ptr* offset */
     if (pkt != NULL) {
-        size_t offset = sizeof(icmpv6_error_param_prob_t);
-        uint8_t *data = pkt->data;
+        icmpv6_error_param_prob_t *hdr = pkt->data;
         uint32_t ptr_offset = 0U;
-        bool found_offset = false;
-
-        while (orig_pkt != NULL) {
-            /* copy as long as it fits into packet */
-            if (offset < pkt->size) {
-                memcpy(data + offset, orig_pkt->data,
-                       MIN(pkt->size - offset, orig_pkt->size));
-                offset += MIN(pkt->size - offset, orig_pkt->size);
-            }
+
+        while (_in_orig_pkt(orig_pkt)) {
+            /* copy as long as it fits into packet; parameter problem can only
+             * come from received packets */
+            size_t offset = _copy_rcv_snip(pkt, orig_pkt);
 
             if (_in_range(ptr, orig_pkt->data, orig_pkt->size)) {
-                ptr_offset += (uint32_t)(((uint8_t *)ptr) - ((uint8_t *)orig_pkt->data));
-                found_offset = true;
-            }
-            else if (!found_offset) {
-                ptr_offset += (uint32_t)orig_pkt->size;
+                ptr_offset = (uint32_t)(((uint8_t *)ptr) -
+                                        ((uint8_t *)orig_pkt->data) +
+                                        (offset - ICMPV6_ERROR_SZ));
             }
-
             orig_pkt = orig_pkt->next;
-
-            if ((offset < pkt->size) && found_offset) {
-                break;
-            }
         }
 
         /* set "pointer" field to relative pointer offset */
-        ((icmpv6_error_param_prob_t *)data)->ptr = byteorder_htonl(ptr_offset);
+        hdr->ptr = byteorder_htonl(ptr_offset);
     }
 
     return pkt;
 }
 
+static void _send(gnrc_pktsnip_t *pkt, const gnrc_pktsnip_t *orig_pkt)
+{
+    if (pkt != NULL) {
+        /* discarding const qualifier is safe here */
+        gnrc_pktsnip_t *ipv6 = gnrc_pktsnip_search_type((gnrc_pktsnip_t *)orig_pkt,
+                                                        GNRC_NETTYPE_IPV6);
+        gnrc_pktsnip_t *netif = gnrc_pktsnip_search_type((gnrc_pktsnip_t *)orig_pkt,
+                                                         GNRC_NETTYPE_NETIF);
+        assert(ipv6 != NULL);
+        ipv6_hdr_t *ipv6_hdr = ipv6->data;
+        ipv6 = gnrc_ipv6_hdr_build(pkt, NULL, &ipv6_hdr->src);
+        if (ipv6 == NULL) {
+            DEBUG("gnrc_icmpv6_error: No space in packet buffer left\n");
+            gnrc_pktbuf_release(pkt);
+            return;
+        }
+        pkt = ipv6;
+        if (netif) {
+            /* copy interface from original netif header to assure packet
+             * goes out where it came from */
+            gnrc_netif_hdr_t *netif_hdr = netif->data;
+            kernel_pid_t netif_pid = netif_hdr->if_pid;
+
+            netif = gnrc_netif_hdr_build(NULL, 0, NULL, 0);
+            if (netif == NULL) {
+                DEBUG("gnrc_icmpv6_error: No space in packet buffer left\n");
+                gnrc_pktbuf_release(pkt);
+                return;
+            }
+            netif_hdr = netif->data;
+            netif_hdr->if_pid = netif_pid;
+            LL_PREPEND(pkt, netif);
+        }
+        if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_IPV6,
+                                       GNRC_NETREG_DEMUX_CTX_ALL,
+                                       pkt)) {
+            DEBUG("gnrc_icmpv6_error: No send handler found.\n");
+            gnrc_pktbuf_release(pkt);
+        }
+    }
+    else {
+        DEBUG("gnrc_icmpv6_error: No space in packet buffer left\n");
+    }
+}
+
+void gnrc_icmpv6_error_dst_unr_send(uint8_t code, const gnrc_pktsnip_t *orig_pkt)
+{
+    gnrc_pktsnip_t *pkt = _dst_unr_build(code, orig_pkt);
+
+    DEBUG("gnrc_icmpv6_error: trying to send destination unreachable error\n");
+    _send(pkt, orig_pkt);
+}
+
+void gnrc_icmpv6_error_pkt_too_big_send(uint32_t mtu,
+                                        const gnrc_pktsnip_t *orig_pkt)
+{
+    gnrc_pktsnip_t *pkt = _pkt_too_big_build(mtu, orig_pkt);
+
+    DEBUG("gnrc_icmpv6_error: trying to send packet too big error\n");
+    _send(pkt, orig_pkt);
+}
+
+void gnrc_icmpv6_error_time_exc_send(uint8_t code,
+                                     const gnrc_pktsnip_t *orig_pkt)
+{
+    gnrc_pktsnip_t *pkt = _time_exc_build(code, orig_pkt);
+
+    DEBUG("gnrc_icmpv6_error: trying to send time exceeded error\n");
+    _send(pkt, orig_pkt);
+}
+
+void gnrc_icmpv6_error_param_prob_send(uint8_t code, void *ptr,
+                                       const gnrc_pktsnip_t *orig_pkt)
+{
+    gnrc_pktsnip_t *pkt = _param_prob_build(code, ptr, orig_pkt);
+
+    DEBUG("gnrc_icmpv6_error: trying to send parameter problem error\n");
+    _send(pkt, orig_pkt);
+}
+
 /** @} */
diff --git a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
index 255095f6391b52faa632881a6508685fb066ff77..b030e39959bfa4f0ef30b1adeebaafa40622f3f0 100644
--- a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
+++ b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
@@ -321,7 +321,8 @@ static void _send_to_iface(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
     ((gnrc_netif_hdr_t *)pkt->data)->if_pid = netif->pid;
     if (gnrc_pkt_len(pkt->next) > netif->ipv6.mtu) {
         DEBUG("ipv6: packet too big\n");
-        gnrc_pktbuf_release(pkt);
+        gnrc_icmpv6_error_pkt_too_big_send(netif->ipv6.mtu, pkt);
+        gnrc_pktbuf_release_error(pkt, EMSGSIZE);
         return;
     }
 #ifdef MODULE_NETSTATS_IPV6
@@ -742,6 +743,7 @@ static void _receive(gnrc_pktsnip_t *pkt)
 #ifdef MODULE_GNRC_IPV6_WHITELIST
         if (!gnrc_ipv6_whitelisted(&((ipv6_hdr_t *)(pkt->data))->src)) {
             DEBUG("ipv6: Source address not whitelisted, dropping packet\n");
+            gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_PROHIB, pkt);
             gnrc_pktbuf_release(pkt);
             return;
         }
@@ -749,6 +751,7 @@ static void _receive(gnrc_pktsnip_t *pkt)
 #ifdef MODULE_GNRC_IPV6_BLACKLIST
         if (gnrc_ipv6_blacklisted(&((ipv6_hdr_t *)(pkt->data))->src)) {
             DEBUG("ipv6: Source address blacklisted, dropping packet\n");
+            gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_PROHIB, pkt);
             gnrc_pktbuf_release(pkt);
             return;
         }
@@ -779,6 +782,7 @@ static void _receive(gnrc_pktsnip_t *pkt)
     else if (!gnrc_ipv6_whitelisted(&((ipv6_hdr_t *)(ipv6->data))->src)) {
         /* if ipv6 header already marked*/
         DEBUG("ipv6: Source address not whitelisted, dropping packet\n");
+        gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_PROHIB, pkt);
         gnrc_pktbuf_release(pkt);
         return;
     }
@@ -787,6 +791,7 @@ static void _receive(gnrc_pktsnip_t *pkt)
     else if (gnrc_ipv6_blacklisted(&((ipv6_hdr_t *)(ipv6->data))->src)) {
         /* if ipv6 header already marked*/
         DEBUG("ipv6: Source address blacklisted, dropping packet\n");
+        gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_PROHIB, pkt);
         gnrc_pktbuf_release(pkt);
         return;
     }
@@ -812,7 +817,9 @@ static void _receive(gnrc_pktsnip_t *pkt)
         DEBUG("ipv6: invalid payload length: %d, actual: %d, dropping packet\n",
               (int) byteorder_ntohs(hdr->len),
               (int) (gnrc_pkt_len_upto(pkt, GNRC_NETTYPE_IPV6) - sizeof(ipv6_hdr_t)));
-        gnrc_pktbuf_release(pkt);
+        gnrc_icmpv6_error_param_prob_send(ICMPV6_ERROR_PARAM_PROB_HDR_FIELD,
+                                          &(hdr->len), pkt);
+        gnrc_pktbuf_release_error(pkt, EINVAL);
         return;
     }
 
@@ -836,6 +843,15 @@ static void _receive(gnrc_pktsnip_t *pkt)
         if ((ipv6_addr_is_link_local(&(hdr->src))) || (ipv6_addr_is_link_local(&(hdr->dst)))) {
             DEBUG("ipv6: do not forward packets with link-local source or"
                   " destination address\n");
+#ifdef MODULE_GNRC_ICMPV6_ERROR
+            if (ipv6_addr_is_link_local(&(hdr->src)) &&
+                !ipv6_addr_is_link_local(&(hdr->dst))) {
+                gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_SCOPE, pkt);
+            }
+            else if (!ipv6_addr_is_multicast(&(hdr->dst))) {
+                gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_ADDR, pkt);
+            }
+#endif
             gnrc_pktbuf_release(pkt);
             return;
         }
@@ -868,7 +884,8 @@ static void _receive(gnrc_pktsnip_t *pkt)
         }
         else {
             DEBUG("ipv6: hop limit reached 0: drop packet\n");
-            gnrc_pktbuf_release(pkt);
+            gnrc_icmpv6_error_time_exc_send(ICMPV6_ERROR_TIME_EXC_HL, pkt);
+            gnrc_pktbuf_release_error(pkt, ETIMEDOUT);
             return;
         }
 
diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c
index 65d3a32c33ec59ab90055397ebc01401bfb1ab04..4c33f9d53685471f333812c434b7ac7abda5df1c 100644
--- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c
+++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c
@@ -17,6 +17,7 @@
 #include <stdbool.h>
 #include <string.h>
 
+#include "net/gnrc/icmpv6/error.h"
 #include "net/gnrc/ipv6.h"
 #include "net/gnrc/ipv6/nib/conf.h"
 #include "net/gnrc/ipv6/nib/nc.h"
@@ -265,6 +266,8 @@ void _nib_nc_remove(_nib_onl_entry_t *node)
          (ptr != NULL) && (tmp = (ptr->next), 1);
          ptr = tmp) {
         gnrc_pktqueue_t *entry = gnrc_pktqueue_remove(&node->pktqueue, ptr);
+        gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_ADDR,
+                                       entry->pkt);
         gnrc_pktbuf_release_error(entry->pkt, EHOSTUNREACH);
         entry->pkt = NULL;
     }
diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib.c b/sys/net/gnrc/network_layer/ipv6/nib/nib.c
index 81eb765d4fe558cc6a29758abe9d9f8485b47c64..17bb9b91aeefc3a37c45410e84fb3c4f015a39a1 100644
--- a/sys/net/gnrc/network_layer/ipv6/nib/nib.c
+++ b/sys/net/gnrc/network_layer/ipv6/nib/nib.c
@@ -18,6 +18,7 @@
 
 #include "log.h"
 #include "net/ipv6/addr.h"
+#include "net/gnrc/icmpv6/error.h"
 #include "net/gnrc/nettype.h"
 #include "net/gnrc/netif/internal.h"
 #include "net/gnrc/ipv6/nib.h"
@@ -203,6 +204,8 @@ int gnrc_ipv6_nib_get_next_hop_l2addr(const ipv6_addr_t *dst,
                  * we also shouldn't release), but if netif is not defined we
                  * should release in any case. */
                 if (netif == NULL) {
+                    gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_ADDR,
+                                                   pkt);
                     gnrc_pktbuf_release_error(pkt, EHOSTUNREACH);
                 }
                 res = -EHOSTUNREACH;
@@ -225,6 +228,8 @@ int gnrc_ipv6_nib_get_next_hop_l2addr(const ipv6_addr_t *dst,
                     memcpy(&route.next_hop, dst, sizeof(route.next_hop));
                 }
                 else {
+                    gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_NO_ROUTE,
+                                                   pkt);
                     res = -ENETUNREACH;
                     gnrc_pktbuf_release_error(pkt, ENETUNREACH);
                     break;
@@ -1168,9 +1173,13 @@ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif_t *netif,
                 }
             }
             else {
+                gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_ADDR,
+                                               pkt);
                 gnrc_pktbuf_release_error(pkt, EHOSTUNREACH);
             }
 #else   /* GNRC_IPV6_NIB_CONF_QUEUE_PKT */
+            gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_ADDR,
+                                           pkt);
             gnrc_pktbuf_release_error(pkt, EHOSTUNREACH);
 #endif  /* GNRC_IPV6_NIB_CONF_QUEUE_PKT */
         }
diff --git a/sys/net/gnrc/transport_layer/udp/gnrc_udp.c b/sys/net/gnrc/transport_layer/udp/gnrc_udp.c
index ceeb4d33a09aa515a755a1abc233d123a64756db..0478ae21fa635966d805ab0d10c5c0c7d4be1ac1 100644
--- a/sys/net/gnrc/transport_layer/udp/gnrc_udp.c
+++ b/sys/net/gnrc/transport_layer/udp/gnrc_udp.c
@@ -27,6 +27,7 @@
 #include "net/ipv6/hdr.h"
 #include "net/gnrc/udp.h"
 #include "net/gnrc.h"
+#include "net/gnrc/icmpv6/error.h"
 #include "net/inet_csum.h"
 
 
@@ -157,6 +158,8 @@ static void _receive(gnrc_pktsnip_t *pkt)
     /* send payload to receivers */
     if (!gnrc_netapi_dispatch_receive(GNRC_NETTYPE_UDP, port, pkt)) {
         DEBUG("udp: unable to forward packet as no one is interested in it\n");
+        /* TODO determine if IPv6 packet, when IPv4 is implemented */
+        gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_PORT, pkt);
         gnrc_pktbuf_release(pkt);
     }
 }