diff --git a/Makefile.dep b/Makefile.dep
index 6dafb9f0e139866ebcf53a826ecbb8a1530f8502..015db06ffb64081ec2749c04dcd12079d7925402 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -223,7 +223,6 @@ ifneq (,$(filter ipv6_ext_rh,$(USEMODULE)))
 endif
 
 ifneq (,$(filter gnrc_ipv6_ext,$(USEMODULE)))
-  USEMODULE += ipv6_ext
   USEMODULE += gnrc_ipv6
 endif
 
diff --git a/sys/include/net/gnrc/ipv6.h b/sys/include/net/gnrc/ipv6.h
index a2ac0e2e30b4eaa7856c872a3457daf27ff67216..deb8536f370384b6a360a8a620833697b3897490 100644
--- a/sys/include/net/gnrc/ipv6.h
+++ b/sys/include/net/gnrc/ipv6.h
@@ -117,11 +117,22 @@ kernel_pid_t gnrc_ipv6_init(void);
  * **Do not use outside this module or its submodules!!!**
  * Public access needed for Extension Headers.
  *
+ * About `current` and `pkt`:
+ *
+ *                     current     pkt
+ *                     |           |
+ *                     v           v
+ * IPv6 <- IPv6_EXT <- IPv6_EXT <- UNDEF
+ *
+ * This situation may happen when the packet has a source routing extension
+ * header (RFC 6554), and the packet is forwarded from an interface to another.
+ *
  * @param[in] iface     The receiving interface.
+ * @param[in] current   A snip to process.
  * @param[in] pkt       A packet.
- * @param[in] nh        A protocol number (see @ref net_protnum).
+ * @param[in] nh        A protocol number (see @ref net_protnum) of the current snip.
  */
-void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, uint8_t nh);
+void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *current, gnrc_pktsnip_t *pkt, uint8_t nh);
 
 #ifdef __cplusplus
 }
diff --git a/sys/include/net/gnrc/ipv6/ext.h b/sys/include/net/gnrc/ipv6/ext.h
index 985eded734eeee2dd684a4f01a534a622504a38d..a7bcdf5396081c562c5d7e83e05b329ec9b52668 100644
--- a/sys/include/net/gnrc/ipv6/ext.h
+++ b/sys/include/net/gnrc/ipv6/ext.h
@@ -40,16 +40,26 @@ extern "C" {
 /**
  * @brief   Demultiplex extension headers according to @p nh.
  *
- * @internal
+ * About `current` and `pkt`:
+ *
+ *                     current     pkt
+ *                     |           |
+ *                     v           v
+ * IPv6 <- IPv6_EXT <- IPv6_EXT <- UNDEF
  *
- * @param[in] iface The receiving interface.
- * @param[in] pkt   A packet.
- * @param[in] nh    A protocol number (see @ref net_protnum).
+ * This situation may happen when the packet has a source routing extension
+ * header (RFC 6554), and the packet is forwarded from an interface to another.
+ *
+ * @internal
  *
- * @return  true, on success - continue packet processing.
- * @return  false, on failure - stop packet processing.
+ * @param[in] iface     The receiving interface.
+ * @param[in] current   A snip to process.
+ * @param[in] pkt       A packet.
+ * @param[in] nh        A protocol number (see @ref net_protnum) of the current snip.
  */
-bool gnrc_ipv6_ext_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt,
+void gnrc_ipv6_ext_demux(kernel_pid_t iface,
+                         gnrc_pktsnip_t *current,
+                         gnrc_pktsnip_t *pkt,
                          uint8_t nh);
 
 /**
diff --git a/sys/include/net/gnrc/nettype.h b/sys/include/net/gnrc/nettype.h
index 7e22ba367b4ac0abb6ddf86024d9b32d786273a6..ea52551f4c6e6f9195075030d52e8015b72ded4f 100644
--- a/sys/include/net/gnrc/nettype.h
+++ b/sys/include/net/gnrc/nettype.h
@@ -63,6 +63,9 @@ typedef enum {
 #ifdef MODULE_GNRC_IPV6
     GNRC_NETTYPE_IPV6,          /**< Protocol is IPv6 */
 #endif
+#ifdef MODULE_GNRC_IPV6_EXT
+    GNRC_NETTYPE_IPV6_EXT,      /**< Protocol is IPv6 extension header */
+#endif
 #ifdef MODULE_GNRC_ICMPV6
     GNRC_NETTYPE_ICMPV6,        /**< Protocol is ICMPv6 */
 #endif
@@ -187,6 +190,16 @@ static inline gnrc_nettype_t gnrc_nettype_from_protnum(uint8_t num)
 #ifdef MODULE_GNRC_UDP
         case PROTNUM_UDP:
             return GNRC_NETTYPE_UDP;
+#endif
+#ifdef MODULE_GNRC_IPV6_EXT
+        case PROTNUM_IPV6_EXT_HOPOPT:
+        case PROTNUM_IPV6_EXT_DST:
+        case PROTNUM_IPV6_EXT_RH:
+        case PROTNUM_IPV6_EXT_FRAG:
+        case PROTNUM_IPV6_EXT_AH:
+        case PROTNUM_IPV6_EXT_ESP:
+        case PROTNUM_IPV6_EXT_MOB:
+            return GNRC_NETTYPE_IPV6_EXT;
 #endif
         default:
             return GNRC_NETTYPE_UNDEF;
diff --git a/sys/include/net/gnrc/pkt.h b/sys/include/net/gnrc/pkt.h
index 2fb788c5389ce5f2acfba8e6ed5b4e0574ff7503..b068ca3b00c56d7848d5126c6f14887e497e19e1 100644
--- a/sys/include/net/gnrc/pkt.h
+++ b/sys/include/net/gnrc/pkt.h
@@ -136,6 +136,31 @@ static inline size_t gnrc_pkt_len(gnrc_pktsnip_t *pkt)
     return len;
 }
 
+/**
+ * @brief Calculates length of a packet in byte upto (including) a snip with the given type.
+ *
+ * @param[in] pkt  list of packet snips.
+ * @param[in] type type of snip to stop calculation.
+ *
+ * @return  length of the list of headers.
+ */
+static inline size_t gnrc_pkt_len_upto(gnrc_pktsnip_t *pkt, gnrc_nettype_t type)
+{
+    size_t len = 0;
+
+    while (pkt) {
+        len += pkt->size;
+
+        if (pkt->type == type) {
+            break;
+        }
+
+        pkt = pkt->next;
+    }
+
+    return len;
+}
+
 /**
  * @brief Count the numbers of snips in the given packet
  *
diff --git a/sys/include/net/gnrc/pktbuf.h b/sys/include/net/gnrc/pktbuf.h
index 420b59c538e67130e5eff3b3d6d8232db4851bcf..f7e11172836dfe26964db5e7e717c3a9203ebeaa 100644
--- a/sys/include/net/gnrc/pktbuf.h
+++ b/sys/include/net/gnrc/pktbuf.h
@@ -215,6 +215,56 @@ gnrc_pktsnip_t *gnrc_pktbuf_remove_snip(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t *sni
  */
 gnrc_pktsnip_t *gnrc_pktbuf_replace_snip(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t *old, gnrc_pktsnip_t *add);
 
+/**
+ * @brief Duplicates pktsnip chain upto (including) a snip with the given type
+ *        as a continuous snip.
+ *
+ *          Example:
+ *              Input:
+ *                                                                  buffer
+ *              +---------------------------+                      +------+
+ *              | size = 8                  | data       +-------->|      |
+ *              | type = NETTYPE_IPV6_EXT   |------------+         +------+
+ *              +---------------------------+                      .      .
+ *                    | next                                       .      .
+ *                    v                                            .      .
+ *              +---------------------------+                      +------+
+ *              | size = 40                 | data    +----------->|      |
+ *              | type = NETTYPE_IPV6       |---------+            +------+
+ *              +---------------------------+                      .      .
+ *                    | next                                       .      .
+ *                    v
+ *              +---------------------------+                      +------+
+ *              | size = 14                 | data +-------------->|      |
+ *              | type = NETTYPE_NETIF      |------+               +------+
+ *              +---------------------------+                      .      .
+ *
+ *
+ *              Output:
+ *                                                                  buffer
+ *              +---------------------------+                      +------+
+ *              | size = 48                 | data       +-------->|      |
+ *              | type = NETTYPE_IPV6       |------------+         |      |
+ *              +---------------------------+                      |      |
+ *                    |                                            +------+
+ *                    |                                            .      .
+ *                    | next                                       .      .
+ *                    v
+ *              +---------------------------+                      +------+
+ *              | size = 14                 | data +-------------->|      |
+ *              | type = NETTYPE_NETIF      |------+               +------+
+ *              +---------------------------+                      .      .
+ *
+ *        The original snip is keeped as is except `users` decremented.
+ *
+ * @param[in,out] pkt   The snip to duplicate.
+ * @param[in]     type  The type of snip to stop duplication.
+ *
+ * @return The duplicated snip, if succeeded.
+ * @return NULL, if no space is left in the packet buffer.
+ */
+gnrc_pktsnip_t *gnrc_pktbuf_duplicate_upto(gnrc_pktsnip_t *pkt, gnrc_nettype_t type);
+
 #ifdef DEVELHELP
 /**
  * @brief   Prints some statistics about the packet buffer to stdout.
diff --git a/sys/net/gnrc/network_layer/ipv6/ext/gnrc_ipv6_ext.c b/sys/net/gnrc/network_layer/ipv6/ext/gnrc_ipv6_ext.c
index 64dd90ca71a76e29a47507d2a61b4b9ed9ffe7fa..fe0936f29fd668f19f75bcc715790c4707ec50e0 100644
--- a/sys/net/gnrc/network_layer/ipv6/ext/gnrc_ipv6_ext.c
+++ b/sys/net/gnrc/network_layer/ipv6/ext/gnrc_ipv6_ext.c
@@ -23,81 +23,163 @@
 #define ENABLE_DEBUG    (0)
 #include "debug.h"
 
-bool gnrc_ipv6_ext_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt,
+#ifdef MODULE_GNRC_RPL_SRH
+
+enum gnrc_ipv6_ext_demux_status {
+    GNRC_IPV6_EXT_OK,
+    GNRC_IPV6_EXT_FORWARDED,
+    GNRC_IPV6_EXT_ERROR,
+};
+
+static enum gnrc_ipv6_ext_demux_status _handle_rh(gnrc_pktsnip_t *current, gnrc_pktsnip_t *pkt)
+{
+    gnrc_pktsnip_t *ipv6;
+    ipv6_ext_t *ext = (ipv6_ext_t *) current->data;
+    size_t current_offset;
+    ipv6_hdr_t *hdr;
+
+    /* check seg_left early to avoid duplicating the packet */
+    if (((ipv6_ext_rh_t *)ext)->seg_left == 0) {
+        return GNRC_IPV6_EXT_OK;
+    }
+
+    /* We cannot use `gnrc_pktbuf_start_write` since it duplicates only
+       the head. `ipv6_ext_rh_process` modifies the IPv6 header as well as
+       the extension header */
+
+    current_offset = gnrc_pkt_len_upto(current->next, GNRC_NETTYPE_IPV6);
+
+    if (pkt->users != 1) {
+        if ((ipv6 = gnrc_pktbuf_duplicate_upto(pkt, GNRC_NETTYPE_IPV6)) == NULL) {
+            DEBUG("ipv6: could not get a copy of pkt\n");
+            gnrc_pktbuf_release(pkt);
+            return GNRC_IPV6_EXT_ERROR;
+        }
+        pkt = ipv6;
+        hdr = ipv6->data;
+        ext = (ipv6_ext_t *)(((uint8_t *)ipv6->data) + current_offset);
+    }
+    else {
+        ipv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6);
+        hdr = ipv6->data;
+    }
+
+    switch (ipv6_ext_rh_process(hdr, (ipv6_ext_rh_t *)ext)) {
+        case EXT_RH_CODE_ERROR:
+            /* TODO: send ICMPv6 error codes */
+            gnrc_pktbuf_release(pkt);
+            return GNRC_IPV6_EXT_ERROR;
+
+        case EXT_RH_CODE_FORWARD:
+            /* forward packet */
+            if (!gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, GNRC_NETREG_DEMUX_CTX_ALL, pkt)) {
+                DEBUG("ipv6: could not dispatch packet to the ipv6 thread\n");
+                gnrc_pktbuf_release(pkt);
+            }
+            return GNRC_IPV6_EXT_FORWARDED;
+
+        case EXT_RH_CODE_OK:
+            /* this should not happen since we checked seg_left early */
+            gnrc_pktbuf_release(pkt);
+            return GNRC_IPV6_EXT_ERROR;
+    }
+
+    return GNRC_IPV6_EXT_OK;
+}
+
+#endif
+
+/*
+ *         current                 pkt
+ *         |                       |
+ *         v                       v
+ * IPv6 <- IPv6_EXT <- IPv6_EXT <- UNDEF
+ */
+void gnrc_ipv6_ext_demux(kernel_pid_t iface,
+                         gnrc_pktsnip_t *current,
+                         gnrc_pktsnip_t *pkt,
                          uint8_t nh)
 {
-    gnrc_pktsnip_t *ext_snip, *tmp;
+    gnrc_pktsnip_t *ext_snip, *tmp, *next;
     ipv6_ext_t *ext;
-    unsigned int offset = 0;
-    ipv6_hdr_t *hdr;
-    int res;
-
-    ext = ((ipv6_ext_t *)(((uint8_t *)pkt->data) + sizeof(ipv6_hdr_t)));
-
-    bool c = true;
-
-    while (c) {
-        switch (nh) {
-            case PROTNUM_IPV6_EXT_HOPOPT:
-            case PROTNUM_IPV6_EXT_DST:
-            case PROTNUM_IPV6_EXT_RH:
-                if ((tmp = gnrc_pktbuf_start_write(pkt)) == NULL) {
-                    DEBUG("ipv6: could not get a copy of pkt\n");
-                    gnrc_pktbuf_release(pkt);
-                    return false;
-                }
-                pkt = tmp;
-                hdr = pkt->data;
-                ext = (ipv6_ext_t *) (((uint8_t *) pkt->data) + sizeof(ipv6_hdr_t) + offset);
-                res = ipv6_ext_rh_process(hdr, (ipv6_ext_rh_t *)ext);
-                if (res == EXT_RH_CODE_ERROR) {
-                    /* TODO: send ICMPv6 error codes */
-                    gnrc_pktbuf_release(pkt);
-                    return false;
-                }
-                else if (res == EXT_RH_CODE_FORWARD) {
-                    /* forward packet */
-                    if (!gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, GNRC_NETREG_DEMUX_CTX_ALL,
-                                                      pkt)) {
-                        DEBUG("ipv6: could not dispatch packet to the ipv6 thread\n");
-                        gnrc_pktbuf_release(pkt);
-                    }
-                    return false;
-                }
-                else if (res == EXT_RH_CODE_OK) {
+    size_t offset = 0;
+
+    ext = (ipv6_ext_t *) current->data;
+
+    switch (nh) {
+        case PROTNUM_IPV6_EXT_RH:
+#ifdef MODULE_GNRC_RPL_SRH
+            switch (_handle_rh(current, pkt)) {
+                case GNRC_IPV6_EXT_OK:
+                    /* We are the final destination. So proceeds like normal packet. */
                     nh = ext->nh;
-                    offset += ((ext->len * IPV6_EXT_LEN_UNIT) + IPV6_EXT_LEN_UNIT);
-                    ext = ipv6_ext_get_next((ipv6_ext_t *)ext);
-                }
-                break;
-            case PROTNUM_IPV6_EXT_FRAG:
-            case PROTNUM_IPV6_EXT_AH:
-            case PROTNUM_IPV6_EXT_ESP:
-            case PROTNUM_IPV6_EXT_MOB:
-                /* TODO: add handling of types */
-                nh = ext->nh;
-                offset += ((ext->len * IPV6_EXT_LEN_UNIT) + IPV6_EXT_LEN_UNIT);
-                ext = ipv6_ext_get_next((ipv6_ext_t *)ext);
-                break;
+                    DEBUG("ipv6_ext: next header = %" PRIu8 "\n", nh);
+                    offset = ((ext->len * IPV6_EXT_LEN_UNIT) + IPV6_EXT_LEN_UNIT);
+                    break;
+
+                case GNRC_IPV6_EXT_ERROR:
+                    /* already released by _handle_rh, so no release here */
+                    return;
+
+                case GNRC_IPV6_EXT_FORWARDED:
+                    return;
+            }
+
+            break;
+#endif
+
+        case PROTNUM_IPV6_EXT_HOPOPT:
+        case PROTNUM_IPV6_EXT_DST:
+        case PROTNUM_IPV6_EXT_FRAG:
+        case PROTNUM_IPV6_EXT_AH:
+        case PROTNUM_IPV6_EXT_ESP:
+        case PROTNUM_IPV6_EXT_MOB:
+            /* TODO: add handling of types */
+            nh = ext->nh;
+            DEBUG("ipv6_ext: next header = %" PRIu8 "\n", nh);
+            offset = ((ext->len * IPV6_EXT_LEN_UNIT) + IPV6_EXT_LEN_UNIT);
+            break;
+
+        default:
+            DEBUG("ipv6_ext: unknown next header: %" PRIu8 "\n", nh);
+            gnrc_pktbuf_release(pkt);
+            return;
+    }
 
-            default:
-                c = false;
-                offset += ((ext->len * IPV6_EXT_LEN_UNIT) + IPV6_EXT_LEN_UNIT);
-                ext = ipv6_ext_get_next((ipv6_ext_t *)ext);
-                break;
+    if (current == pkt) {
+        if ((tmp = gnrc_pktbuf_start_write(pkt)) == NULL) {
+            DEBUG("ipv6: could not get a copy of pkt\n");
+            gnrc_pktbuf_release(pkt);
+            return;
+        }
+        pkt = tmp;
+
+        ext_snip = gnrc_pktbuf_mark(pkt, offset, GNRC_NETTYPE_IPV6_EXT);
+        next = pkt;
+
+        if (ext_snip == NULL) {
+            gnrc_pktbuf_release(pkt);
+            return;
         }
     }
+    else {
+        /* the header is already marked */
+
+        next = NULL;
 
-    ext_snip = gnrc_pktbuf_mark(pkt, offset, GNRC_NETTYPE_IPV6);
+        for (tmp = pkt; tmp != NULL; tmp = tmp->next) {
+            if (tmp->next == current) {
+                next = tmp;
+                break;
+            }
+        }
 
-    if (ext_snip == NULL) {
-        gnrc_pktbuf_release(pkt);
-        return false;
+        assert(next != NULL);
     }
 
-    gnrc_ipv6_demux(iface, pkt, nh);    /* demultiplex next header */
+    gnrc_ipv6_demux(iface, next, pkt, nh);    /* demultiplex next header */
 
-    return true;
+    return;
 }
 
 gnrc_pktsnip_t *gnrc_ipv6_ext_build(gnrc_pktsnip_t *ipv6, gnrc_pktsnip_t *next,
diff --git a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
index 9a81fce010a22538502506190a1aa88510814be7..fa70ab10bf24785624dd280ac7b79006d3ac1923 100644
--- a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
+++ b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
@@ -100,17 +100,24 @@ kernel_pid_t gnrc_ipv6_init(void)
     return gnrc_ipv6_pid;
 }
 
-void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, uint8_t nh)
+/*
+ *         current                 pkt
+ *         |                       |
+ *         v                       v
+ * IPv6 <- IPv6_EXT <- IPv6_EXT <- UNDEF
+ */
+void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *current, gnrc_pktsnip_t *pkt, uint8_t nh)
 {
     int receiver_num;
+    bool interested = false;
 
     pkt->type = gnrc_nettype_from_protnum(nh);
 
     switch (nh) {
 #ifdef MODULE_GNRC_ICMPV6
         case PROTNUM_ICMPV6:
-            DEBUG("ipv6: handle ICMPv6 packet (nh = %u)\n", nh);
-            gnrc_icmpv6_demux(iface, pkt);
+            assert(current == pkt);
+            interested = true;
             break;
 #endif
 #ifdef MODULE_GNRC_IPV6_EXT
@@ -121,15 +128,14 @@ void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, uint8_t nh)
         case PROTNUM_IPV6_EXT_AH:
         case PROTNUM_IPV6_EXT_ESP:
         case PROTNUM_IPV6_EXT_MOB:
-            DEBUG("ipv6: handle extension header (nh = %u)\n", nh);
-            if (!gnrc_ipv6_ext_demux(iface, pkt, nh)) {
-                DEBUG("ipv6: stop packet processing.\n");
-                return;
-            }
+            interested = true;
+
+            break;
 #endif
         case PROTNUM_IPV6:
-            DEBUG("ipv6: handle encapsulated IPv6 packet (nh = %u)\n", nh);
-            _decapsulate(pkt);
+            assert(current == pkt);
+            interested = true;
+
             break;
         default:
             (void)iface;
@@ -142,17 +148,64 @@ void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt, uint8_t nh)
 
     if (receiver_num == 0) {
         DEBUG("ipv6: unable to forward packet as no one is interested in it\n");
-        gnrc_pktbuf_release(pkt);
-        return;
+
+        if (!interested) {
+            assert(current == pkt);
+            gnrc_pktbuf_release(pkt);
+            return;
+        }
+    }
+    else {
+        if (!interested) {
+            assert(current == pkt);
+            /* IPv6 is not interested anymore so `- 1` */
+            receiver_num--;
+        }
+
+        gnrc_pktbuf_hold(current, receiver_num);
+
+        /* XXX can't use gnrc_netapi_dispatch_receive() twice here since a call to that function
+         *     implicitly hands all rights to the packet to one of the receiving threads. As a
+         *     result, the second call to gnrc_netapi_dispatch_receive() would be invalid */
+        _dispatch_rcv_pkt(current->type, GNRC_NETREG_DEMUX_CTX_ALL, current);
+        _dispatch_rcv_pkt(GNRC_NETTYPE_IPV6, nh, current);
+
+        if (!interested) {
+            return;
+        }
     }
 
-    gnrc_pktbuf_hold(pkt, receiver_num - 1);    /* IPv6 is not interested anymore so `- 1` */
+    switch (nh) {
+#ifdef MODULE_GNRC_ICMPV6
+    case PROTNUM_ICMPV6:
+        DEBUG("ipv6: handle ICMPv6 packet (nh = %u)\n", nh);
+        gnrc_icmpv6_demux(iface, pkt);
+        break;
+#endif
+#ifdef MODULE_GNRC_IPV6_EXT
+    case PROTNUM_IPV6_EXT_HOPOPT:
+    case PROTNUM_IPV6_EXT_DST:
+    case PROTNUM_IPV6_EXT_RH:
+    case PROTNUM_IPV6_EXT_FRAG:
+    case PROTNUM_IPV6_EXT_AH:
+    case PROTNUM_IPV6_EXT_ESP:
+    case PROTNUM_IPV6_EXT_MOB:
+        DEBUG("ipv6: handle extension header (nh = %u)\n", nh);
+
+        gnrc_ipv6_ext_demux(iface, current, pkt, nh);
 
-    /* XXX can't use gnrc_netapi_dispatch_receive() twice here since a call to that function
-     *     implicitly hands all rights to the packet to one of the receiving threads. As a result,
-     *     the second call to gnrc_netapi_dispatch_receive() would be invalid */
-    _dispatch_rcv_pkt(pkt->type, GNRC_NETREG_DEMUX_CTX_ALL, pkt);
-    _dispatch_rcv_pkt(GNRC_NETTYPE_IPV6, nh, pkt);
+        return;
+#endif
+    case PROTNUM_IPV6:
+        DEBUG("ipv6: handle encapsulated IPv6 packet (nh = %u)\n", nh);
+        _decapsulate(pkt);
+        return;
+    default:
+        break;
+    }
+
+    assert(current == pkt);
+    gnrc_pktbuf_release(pkt);
 }
 
 /* internal functions */
@@ -704,7 +757,7 @@ static void _dispatch_rcv_pkt(gnrc_nettype_t type, uint32_t demux_ctx,
 static void _receive(gnrc_pktsnip_t *pkt)
 {
     kernel_pid_t iface = KERNEL_PID_UNDEF;
-    gnrc_pktsnip_t *ipv6, *netif;
+    gnrc_pktsnip_t *ipv6, *netif, *first_ext;
     ipv6_hdr_t *hdr;
 
     assert(pkt != NULL);
@@ -715,11 +768,15 @@ static void _receive(gnrc_pktsnip_t *pkt)
         iface = ((gnrc_netif_hdr_t *)netif->data)->if_pid;
     }
 
+    first_ext = pkt;
+
     for (ipv6 = pkt; ipv6 != NULL; ipv6 = ipv6->next) { /* find IPv6 header if already marked */
         if ((ipv6->type == GNRC_NETTYPE_IPV6) && (ipv6->size == sizeof(ipv6_hdr_t)) &&
             (ipv6_hdr_is(ipv6->data))) {
             break;
         }
+
+        first_ext = ipv6;
     }
 
     if (ipv6 == NULL) {
@@ -755,6 +812,8 @@ static void _receive(gnrc_pktsnip_t *pkt)
 
         ipv6 = gnrc_pktbuf_mark(pkt, sizeof(ipv6_hdr_t), GNRC_NETTYPE_IPV6);
 
+        first_ext = pkt;
+
         if (ipv6 == NULL) {
             DEBUG("ipv6: error marking IPv6 header, dropping packet\n");
             gnrc_pktbuf_release(pkt);
@@ -863,7 +922,7 @@ static void _receive(gnrc_pktsnip_t *pkt)
     }
 
     /* IPv6 internal demuxing (ICMPv6, Extension headers etc.) */
-    gnrc_ipv6_demux(iface, pkt, hdr->nh);
+    gnrc_ipv6_demux(iface, first_ext, pkt, hdr->nh);
 }
 
 static void _decapsulate(gnrc_pktsnip_t *pkt)
diff --git a/sys/net/gnrc/pktbuf_static/gnrc_pktbuf_static.c b/sys/net/gnrc/pktbuf_static/gnrc_pktbuf_static.c
index 0bc89cc127dec2d42fc96036377e603dea1bad00..298aa2abab26dae2771da3a86b56b93ae38bdfcf 100644
--- a/sys/net/gnrc/pktbuf_static/gnrc_pktbuf_static.c
+++ b/sys/net/gnrc/pktbuf_static/gnrc_pktbuf_static.c
@@ -217,9 +217,8 @@ void gnrc_pktbuf_hold(gnrc_pktsnip_t *pkt, unsigned int num)
     mutex_unlock(&_mutex);
 }
 
-void gnrc_pktbuf_release_error(gnrc_pktsnip_t *pkt, uint32_t err)
+static void _release_error_locked(gnrc_pktsnip_t *pkt, uint32_t err)
 {
-    mutex_lock(&_mutex);
     while (pkt) {
         gnrc_pktsnip_t *tmp;
         assert(_pktbuf_contains(pkt));
@@ -236,6 +235,12 @@ void gnrc_pktbuf_release_error(gnrc_pktsnip_t *pkt, uint32_t err)
         gnrc_neterr_report(pkt, err);
         pkt = tmp;
     }
+}
+
+void gnrc_pktbuf_release_error(gnrc_pktsnip_t *pkt, uint32_t err)
+{
+    mutex_lock(&_mutex);
+    _release_error_locked(pkt, err);
     mutex_unlock(&_mutex);
 }
 
@@ -526,4 +531,54 @@ gnrc_pktsnip_t *gnrc_pktbuf_replace_snip(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t *ol
     return pkt;
 }
 
+gnrc_pktsnip_t *gnrc_pktbuf_duplicate_upto(gnrc_pktsnip_t *pkt, gnrc_nettype_t type)
+{
+    mutex_lock(&_mutex);
+
+    bool is_shared = pkt->users > 1;
+    size_t size = gnrc_pkt_len_upto(pkt, type);
+
+    DEBUG("ipv6_ext: duplicating %d octets\n", (int) size);
+
+    gnrc_pktsnip_t *tmp;
+    gnrc_pktsnip_t *target = gnrc_pktsnip_search_type(pkt, type);
+    gnrc_pktsnip_t *next = (target == NULL) ? NULL : target->next;
+    gnrc_pktsnip_t *new = _create_snip(next, NULL, size, type);
+
+    if (new == NULL) {
+        mutex_unlock(&_mutex);
+
+        return NULL;
+    }
+
+    /* copy payloads */
+    for (tmp = pkt; tmp != NULL; tmp = tmp->next) {
+        uint8_t *dest = ((uint8_t *)new->data) + (size - tmp->size);
+
+        memcpy(dest, tmp->data, tmp->size);
+
+        size -= tmp->size;
+
+        if (tmp->type == type) {
+            break;
+        }
+    }
+
+    /* decrements reference counters */
+
+    if (target != NULL) {
+        target->next = NULL;
+    }
+
+    _release_error_locked(pkt, GNRC_NETERR_SUCCESS);
+
+    if (is_shared && (target != NULL)) {
+        target->next = next;
+    }
+
+    mutex_unlock(&_mutex);
+
+    return new;
+}
+
 /** @} */
diff --git a/sys/net/gnrc/routing/rpl/srh/gnrc_rpl_srh.c b/sys/net/gnrc/routing/rpl/srh/gnrc_rpl_srh.c
index da97c7a56896333059efc4a44dc821b272070d47..0bc57287a980a9d564b3dcac00f3286c390271f5 100644
--- a/sys/net/gnrc/routing/rpl/srh/gnrc_rpl_srh.c
+++ b/sys/net/gnrc/routing/rpl/srh/gnrc_rpl_srh.c
@@ -70,7 +70,6 @@ int gnrc_rpl_srh_process(ipv6_hdr_t *ipv6, gnrc_rpl_srh_t *rh)
         if (k == n - 1) {
             tmp_pref_elided = GNRC_RPL_SRH_COMPRE(rh->compr);
             tmp_addr_len = sizeof(ipv6_addr_t) - tmp_pref_elided;
-            tmp = ipv6->dst;
         }
         memcpy(&tmp.u8[tmp_pref_elided], &addr_vec[k * compri_addr_len], tmp_addr_len);
         if (gnrc_ipv6_netif_find_by_addr(NULL, &tmp) != KERNEL_PID_UNDEF) {
diff --git a/tests/gnrc_ipv6_ext/Makefile b/tests/gnrc_ipv6_ext/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..3a4db2d10e3a192fce10988f4de140d35f3f6c8a
--- /dev/null
+++ b/tests/gnrc_ipv6_ext/Makefile
@@ -0,0 +1,43 @@
+# name of your application
+APPLICATION = gnrc_ipv6_ext
+
+# If no BOARD is found in the environment, use this default:
+BOARD ?= native
+
+# This has to be the absolute path to the RIOT base directory:
+RIOTBASE ?= $(CURDIR)/../..
+
+BOARD_INSUFFICIENT_MEMORY := airfy-beacon chronos msb-430 msb-430h nrf51dongle \
+                          nrf6310 nucleo-f103 nucleo-f334 pca10000 pca10005 spark-core \
+                          stm32f0discovery telosb weio wsn430-v1_3b wsn430-v1_4 \
+                          yunjia-nrf51822 z1
+
+# Include packages that pull up and auto-init the link layer.
+# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
+USEMODULE += gnrc_netif_default
+USEMODULE += auto_init_gnrc_netif
+# Specify the mandatory networking modules for IPv6
+USEMODULE += gnrc_ipv6_router_default
+# IPv6 extension headers
+USEMODULE += gnrc_ipv6_ext
+USEMODULE += gnrc_rpl_srh
+# Add also the shell, some shell commands
+USEMODULE += shell
+USEMODULE += shell_commands
+USEMODULE += ps
+
+# Comment this out to disable code in RIOT that does safety checking
+# which is not needed in a production environment but helps in the
+# development process:
+CFLAGS += -DDEVELHELP
+
+# Change this to 0 show compiler invocation lines by default:
+QUIET ?= 1
+
+include $(RIOTBASE)/Makefile.include
+
+# This requires ENABLE_DEBUG in gnrc_ipv6.c to be 1
+test:
+# `testrunner` calls `make term` recursively, results in duplicated `TERMFLAGS`.
+# So clears `TERMFLAGS` before run.
+	TERMFLAGS= tests/01-run.py
diff --git a/tests/gnrc_ipv6_ext/README.md b/tests/gnrc_ipv6_ext/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..89650e40fb30bb7d0e133699585441ea4ef393ef
--- /dev/null
+++ b/tests/gnrc_ipv6_ext/README.md
@@ -0,0 +1,43 @@
+# `gnrc_ipv6_ext` test
+
+This test sends a packet to itself with extension headers. This is based on gnrc_networking example.
+
+Enable debug output of `gnrc_ipv6.c` before run. When the test is run, it should show the following debug output:
+
+```
+ipv6: Received (src = fd01::1, dst = fd01::2, next header = 0, length = 40)
+ipv6: forward nh = 0 to other threads
+ipv6: unable to forward packet as no one is interested in it
+ipv6: handle extension header (nh = 0)
+ipv6: forward nh = 43 to other threads
+ipv6: unable to forward packet as no one is interested in it
+ipv6: handle extension header (nh = 43)
+ipv6: waiting for incoming message.
+ipv6: GNRC_NETAPI_MSG_TYPE_RCV received
+ipv6: Received (src = fd01::1, dst = fd01::3, next header = 0, length = 40)
+ipv6: forward nh = 0 to other threads
+ipv6: unable to forward packet as no one is interested in it
+ipv6: handle extension header (nh = 0)
+ipv6: forward nh = 43 to other threads
+ipv6: unable to forward packet as no one is interested in it
+ipv6: handle extension header (nh = 43)
+ipv6: waiting for incoming message.
+ipv6: GNRC_NETAPI_MSG_TYPE_RCV received
+ipv6: Received (src = fd01::1, dst = fd01::2, next header = 0, length = 40)
+ipv6: forward nh = 0 to other threads
+ipv6: unable to forward packet as no one is interested in it
+ipv6: handle extension header (nh = 0)
+ipv6: forward nh = 43 to other threads
+ipv6: unable to forward packet as no one is interested in it
+ipv6: handle extension header (nh = 43)
+ipv6: forward nh = 17 to other threads
+ipv6: Send receive command for 0x1060b8 to 5
+ipv6: waiting for incoming message.
+pkt->users: 0
+```
+
+It configures the network interface with addresses fd01::02 and fd01::03. Then it sends a packet to fd01::02 with a routing extension header containing addresses fd01::03 and fd01::02. So the packet should be forwarded from fd01::02 to fd01::03, then to fd01::02 again.
+
+The packet has a Hop-by-Hop extension header that should be ignored.
+
+The test also asserts that the packet is released.
diff --git a/tests/gnrc_ipv6_ext/main.c b/tests/gnrc_ipv6_ext/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..ddf0c939aeba06306806d4a93ae2b822165ae7f4
--- /dev/null
+++ b/tests/gnrc_ipv6_ext/main.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+/**
+ * @ingroup     tests
+ * @{
+ *
+ * @file
+ * @brief       Tests extension header handling of gnrc stack.
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ * @author      Takuo Yonezawa <Yonezawa-T2@mail.dnp.co.jp>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+
+#include "shell.h"
+#include "msg.h"
+#include "net/ipv6/addr.h"
+#include "net/gnrc/ipv6/netif.h"
+#include "net/gnrc/pkt.h"
+#include "net/gnrc/pktbuf.h"
+#include "net/gnrc/netreg.h"
+#include "net/gnrc/netapi.h"
+#include "net/gnrc/netif.h"
+
+#define MAIN_QUEUE_SIZE     (8)
+static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
+
+static void _send_packet(void) {
+    kernel_pid_t ifs[GNRC_NETIF_NUMOF];
+    ipv6_addr_t addr = IPV6_ADDR_UNSPECIFIED;
+
+    gnrc_netif_get(ifs);
+
+    addr.u8[0] = 0xfd;
+    addr.u8[1] = 0x01;
+    addr.u8[15] = 0x02;
+    /* fd01::02 */
+    gnrc_ipv6_netif_add_addr(ifs[0], &addr, 64, GNRC_IPV6_NETIF_ADDR_FLAGS_UNICAST);
+
+    addr.u8[15] = 0x03;
+    /* fd01::03 */
+    gnrc_ipv6_netif_add_addr(ifs[0], &addr, 64, GNRC_IPV6_NETIF_ADDR_FLAGS_UNICAST);
+
+    uint8_t data[] = {
+        /* IPv6 Header */
+        0x60, 0x00, 0x00, 0x00, /* version, traffic class, flow label */
+        0x00, 0x28, /* payload length: 40 */
+        0x00, /* next header: Hop-by-Hop Option */
+        0x10, /* hop limit: 16 */
+        /* source address: fd01::1 */
+        0xfd, 0x01, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01,
+        /* destination address: fd01::2 */
+        0xfd, 0x01, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x02,
+
+        /* Hop-by-Hop Options Header */
+        /* https://tools.ietf.org/html/rfc6553 */
+        0x2b, /* next header: IPv6-Route */
+        0x00, /* hdr ext len: 0 * 8 + 8 = 8 */
+        0x63, /* option type: RPL Option */
+        0x04, /* opt data len: 4 */
+        0x80, /* flags, Down: 1, Rank-Error: 0, Forwarding-Error: 0 */
+        0x00, /* RPLInstanceID */
+        0x80, 0x00, /* SenderRank */
+
+        /* RPL Routing Header */
+        /* https://tools.ietf.org/html/rfc6554 */
+        0x11, /* next header: UDP */
+        0x02, /* hdr ext len: 2 * 8 + 8 = 24 */
+        0x03, /* routing type: SRH */
+        0x02, /* segments left: 2 */
+        0xef, /* ComprI: 14, ComprE: 15 */
+        0xd0, 0x00, 0x00, /* pad and reserved */
+        /* address: fd01::3, fd01::2 */
+        0x00, 0x03, 0x02, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+
+        /* UDP (ignored) */
+        0x1f, 0x90, /* source port: 8080 */
+        0x1f, 0x90, /* destination port: 8080 */
+        0x00, 0x08, /* length: 8 */
+        0xff, 0xff, /* checksum */
+    };
+
+    gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, data, sizeof(data), GNRC_NETTYPE_UNDEF);
+
+    gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, GNRC_NETREG_DEMUX_CTX_ALL, pkt);
+
+    printf("pkt->users: %d\n", pkt->users);
+    assert(pkt->users == 0);
+}
+
+int main(void)
+{
+    /* we need a message queue for the thread running the shell in order to
+     * receive potentially fast incoming networking packets */
+    msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);
+    puts("RIOT network stack example application");
+
+    _send_packet();
+
+    /* start shell */
+    puts("All up, running the shell now");
+    char line_buf[SHELL_DEFAULT_BUFSIZE];
+    shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);
+
+    /* should be never reached */
+    return 0;
+}
diff --git a/tests/gnrc_ipv6_ext/tests/01-run.py b/tests/gnrc_ipv6_ext/tests/01-run.py
new file mode 100755
index 0000000000000000000000000000000000000000..0e50cb66ddeaf9a6ee85c4578122a968ed4246b6
--- /dev/null
+++ b/tests/gnrc_ipv6_ext/tests/01-run.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de>
+# Copyright (C) 2016 Takuo Yonezawa <Yonezawa-T2@mail.dnp.co.jp>
+#
+# 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.
+
+import os
+import sys
+
+sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
+import testrunner
+
+def testfunc(child):
+    index = child.expect_exact([
+        "ipv6: Received (src = fd01::1, dst = fd01::2, next header = 0, length = 40)",
+        "pkt->users: 0"
+    ])
+
+    if index == 1:
+        # debug is disabled
+        return
+
+    child.expect_exact("ipv6: handle extension header (nh = 0)")
+    child.expect_exact("ipv6: handle extension header (nh = 43)")
+    child.expect_exact("ipv6: Received (src = fd01::1, dst = fd01::3, next header = 0, length = 40)")
+    child.expect_exact("ipv6: handle extension header (nh = 0)")
+    child.expect_exact("ipv6: handle extension header (nh = 43)")
+    child.expect_exact("ipv6: Received (src = fd01::1, dst = fd01::2, next header = 0, length = 40)")
+    child.expect_exact("ipv6: handle extension header (nh = 0)")
+    child.expect_exact("ipv6: handle extension header (nh = 43)")
+    child.expect_exact("ipv6: forward nh = 17 to other threads")
+    child.expect_exact("pkt->users: 0")
+
+if __name__ == "__main__":
+    sys.exit(testrunner.run(testfunc))