From 82a3aae8be2ae16cd1b81c0f755fe8b66cc60b0f Mon Sep 17 00:00:00 2001
From: Martine Lenders <mlenders@inf.fu-berlin.de>
Date: Sat, 9 Apr 2016 22:11:21 +0200
Subject: [PATCH] gnrc_ipv6: fix asserts for NHC

---
 .../network_layer/ipv6/ext/gnrc_ipv6_ext.c    | 160 ++++++++++-------
 sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c   | 161 ++++++++++--------
 2 files changed, 183 insertions(+), 138 deletions(-)

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 fe0936f29f..b86d035e7d 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
@@ -89,6 +89,56 @@ static enum gnrc_ipv6_ext_demux_status _handle_rh(gnrc_pktsnip_t *current, gnrc_
 
 #endif
 
+/**
+ * @brief marks IPv6 extension header if needed.
+ *   updates pkt and returns next header.
+ * @param[in] current  The current header
+ * @param[in,out] pkt  The whole packet
+ * @return The next header
+ * @return NULL on error
+ */
+static gnrc_pktsnip_t *_mark_extension_header(gnrc_pktsnip_t *current,
+                                              gnrc_pktsnip_t **pkt)
+{
+    gnrc_pktsnip_t *ext_snip, *tmp, *next;
+    ipv6_ext_t *ext = (ipv6_ext_t *) current->data;
+    size_t offset = ((ext->len * IPV6_EXT_LEN_UNIT) + IPV6_EXT_LEN_UNIT);
+
+    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 NULL;
+        }
+        *pkt = tmp;
+
+        ext_snip = gnrc_pktbuf_mark(*pkt, offset, GNRC_NETTYPE_IPV6_EXT);
+        next = *pkt;
+
+        if (ext_snip == NULL) {
+            gnrc_pktbuf_release(*pkt);
+            return NULL;
+        }
+    }
+    else {
+        /* the header is already marked */
+
+        next = NULL;
+
+        for (tmp = *pkt; tmp != NULL; tmp = tmp->next) {
+            if (tmp->next == current) {
+                next = tmp;
+                break;
+            }
+        }
+
+        assert(next != NULL);
+    }
+
+    return next;
+}
+
+
 /*
  *         current                 pkt
  *         |                       |
@@ -100,86 +150,68 @@ void gnrc_ipv6_ext_demux(kernel_pid_t iface,
                          gnrc_pktsnip_t *pkt,
                          uint8_t nh)
 {
-    gnrc_pktsnip_t *ext_snip, *tmp, *next;
     ipv6_ext_t *ext;
-    size_t offset = 0;
 
-    ext = (ipv6_ext_t *) current->data;
+    while (true) {
+        ext = (ipv6_ext_t *) current->data;
 
-    switch (nh) {
-        case PROTNUM_IPV6_EXT_RH:
+        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;
-                    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;
+                switch (_handle_rh(current, pkt)) {
+                    case GNRC_IPV6_EXT_OK:
+                        /* We are the final destination. So proceeds like normal packet. */
+                        nh = ext->nh;
+                        DEBUG("ipv6_ext: next header = %" PRIu8 "\n", nh);
 
-                case GNRC_IPV6_EXT_FORWARDED:
-                    return;
-            }
+                        if ((current = _mark_extension_header(current, &pkt)) == NULL) {
+                            return;
+                        }
 
-            break;
-#endif
+                        gnrc_ipv6_demux(iface, current, pkt, nh); /* demultiplex next header */
 
-        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;
-    }
+                        return;
 
-    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;
+                    case GNRC_IPV6_EXT_ERROR:
+                        /* already released by _handle_rh, so no release here */
+                        return;
 
-        ext_snip = gnrc_pktbuf_mark(pkt, offset, GNRC_NETTYPE_IPV6_EXT);
-        next = pkt;
+                    case GNRC_IPV6_EXT_FORWARDED:
+                        /* the packet is forwarded and released. finish processing */
+                        return;
+                }
 
-        if (ext_snip == NULL) {
-            gnrc_pktbuf_release(pkt);
-            return;
-        }
-    }
-    else {
-        /* the header is already marked */
+                break;
+#endif
 
-        next = NULL;
+            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);
+
+                if ((current = _mark_extension_header(current, &pkt)) == NULL) {
+                    return;
+                }
+
+                gnrc_pktbuf_hold(pkt, 1);   /* don't release on next dispatch */
+                if (gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, nh, pkt) == 0) {
+                    gnrc_pktbuf_release(pkt);
+                }
 
-        for (tmp = pkt; tmp != NULL; tmp = tmp->next) {
-            if (tmp->next == current) {
-                next = tmp;
                 break;
-            }
-        }
 
-        assert(next != NULL);
+            default:
+                gnrc_ipv6_demux(iface, current, pkt, nh); /* demultiplex next header */
+                return;
+        }
     }
 
-    gnrc_ipv6_demux(iface, next, pkt, nh);    /* demultiplex next header */
-
-    return;
+    assert(false); /* never reaches here */
 }
 
 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 d608dcd3e8..b09cde2d3c 100644
--- a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
+++ b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
@@ -69,9 +69,6 @@ kernel_pid_t gnrc_ipv6_pid = KERNEL_PID_UNDEF;
 
 /* handles GNRC_NETAPI_MSG_TYPE_RCV commands */
 static void _receive(gnrc_pktsnip_t *pkt);
-/* dispatches received IPv6 packet for upper layer */
-static void _dispatch_rcv_pkt(gnrc_nettype_t type, uint32_t demux_ctx,
-                              gnrc_pktsnip_t *pkt);
 /* Sends packet over the appropriate interface(s).
  * prep_hdr: prepare header for sending (call to _fill_ipv6_hdr()), otherwise
  * assume it is already prepared */
@@ -100,6 +97,9 @@ kernel_pid_t gnrc_ipv6_init(void)
     return gnrc_ipv6_pid;
 }
 
+static void _dispatch_next_header(gnrc_pktsnip_t *current, gnrc_pktsnip_t *pkt,
+                                  uint8_t nh, bool interested);
+
 /*
  *         current                 pkt
  *         |                       |
@@ -108,10 +108,9 @@ kernel_pid_t gnrc_ipv6_init(void)
  */
 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);
+    current->type = gnrc_nettype_from_protnum(nh);
 
     switch (nh) {
 #ifdef MODULE_GNRC_ICMPV6
@@ -139,76 +138,106 @@ void gnrc_ipv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *current, gnrc_pktsnip_t
             break;
         default:
             (void)iface;
-            break;
-    }
-
-    DEBUG("ipv6: forward nh = %u to other threads\n", nh);
-    receiver_num = gnrc_netreg_num(pkt->type, GNRC_NETREG_DEMUX_CTX_ALL) +
-                   gnrc_netreg_num(GNRC_NETTYPE_IPV6, nh);
-
-    if (receiver_num == 0) {
-        DEBUG("ipv6: unable to forward packet as no one is interested in it\n");
-
-        if (!interested) {
+#ifdef MODULE_GNRC_SIXLOWPAN_IPHC_NHC
+            /* second statement is true for small 6LoWPAN NHC decompressed frames
+             * since in this case it looks like
+             *
+             * * GNRC_NETTYPE_UNDEF <- pkt
+             * v
+             * * GNRC_NETTYPE_UDP <- current
+             * v
+             * * GNRC_NETTYPE_EXT
+             * v
+             * * GNRC_NETTYPE_IPV6
+             */
+            assert((current == pkt) || (current == pkt->next));
+#else
             assert(current == pkt);
-            gnrc_pktbuf_release(pkt);
-            return;
-        }
+#endif
+            break;
     }
-    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);
+    _dispatch_next_header(current, pkt, nh, interested);
 
-        if (!interested) {
-            return;
-        }
+    if (!interested) {
+        return;
     }
 
     switch (nh) {
 #ifdef MODULE_GNRC_ICMPV6
-    case PROTNUM_ICMPV6:
-        DEBUG("ipv6: handle ICMPv6 packet (nh = %u)\n", nh);
-        gnrc_icmpv6_demux(iface, pkt);
-        break;
+        case PROTNUM_ICMPV6:
+            DEBUG("ipv6: handle ICMPv6 packet (nh = %u)\n", nh);
+            gnrc_icmpv6_demux(iface, pkt);
+            gnrc_pktbuf_release(pkt);
+            return;
 #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);
+        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);
+            gnrc_ipv6_ext_demux(iface, current, pkt, nh);
 
-        return;
+            return;
 #endif
-    case PROTNUM_IPV6:
-        DEBUG("ipv6: handle encapsulated IPv6 packet (nh = %u)\n", nh);
-        _decapsulate(pkt);
-        return;
-    default:
-        break;
+        case PROTNUM_IPV6:
+            DEBUG("ipv6: handle encapsulated IPv6 packet (nh = %u)\n", nh);
+            _decapsulate(pkt);
+            return;
+        default:
+            assert(false);
+            break;
     }
 
-    assert(current == pkt);
-    gnrc_pktbuf_release(pkt);
+    assert(false);
 }
 
 /* internal functions */
+static void _dispatch_next_header(gnrc_pktsnip_t *current, gnrc_pktsnip_t *pkt,
+                                  uint8_t nh, bool interested)
+{
+#ifdef MODULE_GNRC_IPV6_EXT
+    const bool should_dispatch_current_type = ((current->type != GNRC_NETTYPE_IPV6_EXT) ||
+                                               (current->next->type == GNRC_NETTYPE_IPV6));
+#else
+    const bool should_dispatch_current_type = (current->next->type == GNRC_NETTYPE_IPV6);
+#endif
+
+    DEBUG("ipv6: forward nh = %u to other threads\n", nh);
+
+    /* dispatch IPv6 extension header only once */
+    if (should_dispatch_current_type) {
+        bool should_release = (gnrc_netreg_num(GNRC_NETTYPE_IPV6, nh) == 0) &&
+                              (!interested);
+
+        if (!should_release) {
+            gnrc_pktbuf_hold(pkt, 1);   /* don't remove from packet buffer in
+                                         * next dispatch */
+        }
+        if (gnrc_netapi_dispatch_receive(current->type,
+                                         GNRC_NETREG_DEMUX_CTX_ALL,
+                                         pkt) == 0) {
+            gnrc_pktbuf_release(pkt);
+        }
+
+        if (should_release) {
+            return;
+        }
+    }
+    if (interested) {
+        gnrc_pktbuf_hold(pkt, 1);   /* don't remove from packet buffer in
+                                     * next dispatch */
+    }
+    if (gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, nh, pkt) == 0) {
+        gnrc_pktbuf_release(pkt);
+    }
+}
+
 static void *_event_loop(void *args)
 {
     msg_t msg, reply, msg_q[GNRC_IPV6_MSG_QUEUE_SIZE];
@@ -743,22 +772,6 @@ static inline bool _pkt_not_for_me(kernel_pid_t *iface, ipv6_hdr_t *hdr)
     }
 }
 
-static void _dispatch_rcv_pkt(gnrc_nettype_t type, uint32_t demux_ctx,
-                              gnrc_pktsnip_t *pkt)
-{
-    gnrc_netreg_entry_t *entry = gnrc_netreg_lookup(type, demux_ctx);
-
-    while (entry) {
-        DEBUG("ipv6: Send receive command for %p to %" PRIu16 "\n", (void *)pkt,
-              entry->pid);
-        if (gnrc_netapi_receive(entry->pid, pkt) < 1) {
-            DEBUG("ipv6: unable to deliver packet\n");
-            gnrc_pktbuf_release(pkt);
-        }
-        entry = gnrc_netreg_getnext(entry);
-    }
-}
-
 static void _receive(gnrc_pktsnip_t *pkt)
 {
     kernel_pid_t iface = KERNEL_PID_UNDEF;
@@ -868,7 +881,7 @@ static void _receive(gnrc_pktsnip_t *pkt)
          * links."
          */
         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"\
+            DEBUG("ipv6: do not forward packets with link-local source or"
                   " destination address\n");
             gnrc_pktbuf_release(pkt);
             return;
-- 
GitLab