diff --git a/Makefile.dep b/Makefile.dep
index 50f6ad402c52a0e150be5f6e1d8ce44f9c85bc18..c0cb60f92c19c12f1bc24b0be9f8eb25b04c34fe 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -197,6 +197,10 @@ ifneq (,$(filter gnrc_icmpv6_echo,$(USEMODULE)))
   USEMODULE += gnrc_icmpv6
 endif
 
+ifneq (,$(filter gnrc_icmpv6_error,$(USEMODULE)))
+  USEMODULE += gnrc_icmpv6
+endif
+
 ifneq (,$(filter gnrc_icmpv6,$(USEMODULE)))
   USEMODULE += inet_csum
   USEMODULE += gnrc_ipv6
diff --git a/sys/include/net/gnrc/icmpv6/error.h b/sys/include/net/gnrc/icmpv6/error.h
index 7774d9a54f6493dc99b6d1a9ecc6b7966ec3fd8c..798912368c561090bb29922d02be3316e6b80d28 100644
--- a/sys/include/net/gnrc/icmpv6/error.h
+++ b/sys/include/net/gnrc/icmpv6/error.h
@@ -16,16 +16,66 @@
  * @brief   ICMPv6 error message definitions
  *
  * @author  Martine Lenders <mlenders@inf.fu-berlin.de>
- *
- * @todo implement build and handle functions
  */
 #ifndef GNRC_ICMPV6_ERROR_H_
 #define GNRC_ICMPV6_ERROR_H_
 
+#include <stdint.h>
+
+#include "net/icmpv6.h"
+#include "net/ipv6/hdr.h"
+#include "net/gnrc/pkt.h"
+
 #ifdef __cplusplus
 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);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/sys/include/net/icmpv6.h b/sys/include/net/icmpv6.h
index d0b6da4317e578840790f2cbd18e326136ff2738..256bf04c4fc2efd22a029adafb3acf40d9128dc6 100644
--- a/sys/include/net/icmpv6.h
+++ b/sys/include/net/icmpv6.h
@@ -38,7 +38,7 @@ extern "C" {
  *          IANA, ICMPv6 "type" Numbers
  *      </a>
  */
-#define ICMPV6_DEST_UNR     (1)     /**< Destination unreachable message */
+#define ICMPV6_DST_UNR      (1)     /**< Destination unreachable message */
 #define ICMPV6_PKT_TOO_BIG  (2)     /**< Packet Too Big message */
 #define ICMPV6_TIME_EXC     (3)     /**< Time Exceeded message */
 #define ICMPV6_PARAM_PROB   (4)     /**< Parameter Problem message */
diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile
index 54fe9e810768a3e88145264354e3382342c50d67..76c83ddd2d92f169055404904936272121aa3edd 100644
--- a/sys/net/gnrc/Makefile
+++ b/sys/net/gnrc/Makefile
@@ -13,6 +13,9 @@ endif
 ifneq (,$(filter gnrc_icmpv6_echo,$(USEMODULE)))
     DIRS += network_layer/icmpv6/echo
 endif
+ifneq (,$(filter gnrc_icmpv6_error,$(USEMODULE)))
+    DIRS += network_layer/icmpv6/error
+endif
 ifneq (,$(filter gnrc_ipv6,$(USEMODULE)))
     DIRS += network_layer/ipv6
 endif
diff --git a/sys/net/gnrc/network_layer/icmpv6/error/Makefile b/sys/net/gnrc/network_layer/icmpv6/error/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..3ad164ac181f874f3588f0cdf391242e5d720aba
--- /dev/null
+++ b/sys/net/gnrc/network_layer/icmpv6/error/Makefile
@@ -0,0 +1,3 @@
+MODULE = gnrc_icmpv6_error
+
+include $(RIOTBASE)/Makefile.base
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
new file mode 100644
index 0000000000000000000000000000000000000000..80a6773bdd524e9b756fb0c6654e83baba90e9c2
--- /dev/null
+++ b/sys/net/gnrc/network_layer/icmpv6/error/gnrc_icmpv6_error.c
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+/**
+ * @{
+ *
+ * @file
+ */
+
+#include "net/gnrc/pktbuf.h"
+
+#include "net/ipv6.h"
+#include "net/gnrc/ipv6/netif.h"
+#include "net/gnrc/icmpv6/error.h"
+#include "net/gnrc/icmpv6.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)
+{
+    /* TODO: replace IPV6_MIN_MTU with known path MTU? */
+    return MIN((gnrc_pkt_len(pkt) + ICMPV6_ERROR_SZ), IPV6_MIN_MTU);
+}
+
+/* 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)
+{
+    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 */
+    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;
+        }
+    }
+
+    return pkt;
+}
+
+gnrc_pktsnip_t *gnrc_icmpv6_error_dst_unr_build(uint8_t code, 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)
+{
+    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)
+{
+    return _icmpv6_error_build(ICMPV6_TIME_EXC, code, orig_pkt, 0);
+}
+
+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)
+{
+    gnrc_pktsnip_t *pkt = gnrc_icmpv6_build(NULL, ICMPV6_PARAM_PROB, code,
+                                            _fit(orig_pkt));
+
+    /* 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;
+        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);
+            }
+
+            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;
+            }
+
+            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);
+    }
+
+    return pkt;
+}
+
+/** @} */