From a0c6db65913a7caec5d613055e57a2abf0d55e93 Mon Sep 17 00:00:00 2001
From: Martine Lenders <mail@martine-lenders.eu>
Date: Tue, 3 Mar 2015 22:20:21 +0100
Subject: [PATCH] ng_ipv6: initial import

---
 Makefile.dep                            |  13 +
 Makefile.pseudomodules                  |   1 +
 sys/Makefile                            |   3 +
 sys/auto_init/auto_init.c               |   8 +
 sys/include/net/ng_ipv6.h               |  56 +++
 sys/net/network_layer/ng_ipv6/Makefile  |   1 +
 sys/net/network_layer/ng_ipv6/ng_ipv6.c | 525 ++++++++++++++++++++++++
 7 files changed, 607 insertions(+)
 create mode 100644 sys/net/network_layer/ng_ipv6/Makefile
 create mode 100644 sys/net/network_layer/ng_ipv6/ng_ipv6.c

diff --git a/Makefile.dep b/Makefile.dep
index 2c14046231..61498f9d8e 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -58,6 +58,19 @@ ifneq (,$(filter ng_ipv6_hdr,$(USEMODULE)))
   USEMODULE += ng_pktbuf
 endif
 
+ifneq (,$(filter ng_ipv6_router,$(USEMODULE)))
+  USEMODULE += ng_ipv6
+endif
+
+ifneq (,$(filter ng_ipv6,$(USEMODULE)))
+  USEMODULE += ng_inet_csum
+  USEMODULE += ng_ipv6_addr
+  USEMODULE += ng_ipv6_hdr
+  USEMODULE += ng_ipv6_nc
+  USEMODULE += ng_ipv6_netif
+  USEMODULE += ng_netbase
+endif
+
 ifneq (,$(filter ng_ipv6_nc,$(USEMODULE)))
   USEMODULE += ng_ipv6_addr
 endif
diff --git a/Makefile.pseudomodules b/Makefile.pseudomodules
index 36e142a6bb..3536c4ab5e 100644
--- a/Makefile.pseudomodules
+++ b/Makefile.pseudomodules
@@ -1,4 +1,5 @@
 PSEUDOMODULES += defaulttransceiver
 PSEUDOMODULES += transport_layer
+PSEUDOMODULES += ng_ipv6_router
 PSEUDOMODULES += pktqueue
 PSEUDOMODULES += ng_netbase
diff --git a/sys/Makefile b/sys/Makefile
index dd4b6ba0dc..2f3500c967 100644
--- a/sys/Makefile
+++ b/sys/Makefile
@@ -62,6 +62,9 @@ endif
 ifneq (,$(filter oneway_malloc,$(USEMODULE)))
     DIRS += oneway-malloc
 endif
+ifneq (,$(filter ng_ipv6,$(USEMODULE)))
+    DIRS += net/network_layer/ng_ipv6
+endif
 ifneq (,$(filter ng_ipv6_addr,$(USEMODULE)))
     DIRS += net/network_layer/ng_ipv6/addr
 endif
diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c
index 5d427c8d32..a2ec8feebb 100644
--- a/sys/auto_init/auto_init.c
+++ b/sys/auto_init/auto_init.c
@@ -82,6 +82,10 @@
 #include "periph/cpuid.h"
 #endif
 
+#ifdef MODULE_NG_IPV6
+#include "net/ng_ipv6.h"
+#endif
+
 #ifdef MODULE_L2_PING
 #include "l2_ping.h"
 #endif
@@ -280,4 +284,8 @@ void auto_init(void)
     DEBUG("Auto init ng_pktdump module.\n");
     ng_pktdump_init();
 #endif
+#ifdef MODULE_NG_IPV6
+    DEBUG("Auto init ng_ipv6 module.\n");
+    ng_ipv6_init();
+#endif
 }
diff --git a/sys/include/net/ng_ipv6.h b/sys/include/net/ng_ipv6.h
index 3f5c7c8a9a..657a3d14f1 100644
--- a/sys/include/net/ng_ipv6.h
+++ b/sys/include/net/ng_ipv6.h
@@ -10,6 +10,12 @@
  * @defgroup    net_ng_ipv6 IPv6
  * @ingroup     net
  * @brief       New IPv6 implementation
+ *
+ * The IPv6 control thread understands messages of type
+ *
+ *  * @ref NG_NETAPI_MSG_TYPE_RCV, and
+ *  * @ref NG_NETAPI_MSG_TYPE_SND,
+ *
  * @{
  *
  * @file
@@ -22,14 +28,64 @@
 #ifndef NG_IPV6_H_
 #define NG_IPV6_H_
 
+#include "kernel_types.h"
+#include "net/ng_netbase.h"
+#include "thread.h"
+
 #include "net/ng_ipv6/addr.h"
 #include "net/ng_ipv6/hdr.h"
+#include "net/ng_ipv6/nc.h"
 #include "net/ng_ipv6/netif.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/**
+ * @brief   Default stack size to use for the IPv6 thread
+ */
+#ifndef NG_IPV6_STACK_SIZE
+#define NG_IPV6_STACK_SIZE (KERNEL_CONF_STACKSIZE_DEFAULT)
+#endif
+
+/**
+ * @brief   Default name for the IPv6 thread
+ */
+#ifndef NG_IPV6_PRIO
+#define NG_IPV6_PRIO            (PRIORITY_MAIN - 3)
+#endif
+
+/**
+ * @brief   Default message queue size to use for the IPv6 thread.
+ */
+#ifndef NG_IPV6_MSG_QUEUE_SIZE
+#define NG_IPV6_MSG_QUEUE_SIZE  (8U)
+#endif
+
+/**
+ * @brief   Initialization of the IPv6 thread.
+ *
+ * @return  The PID to the IPv6 thread, on success.
+ * @return  a negative errno on error.
+ * @return  -EOVERFLOW, if there are too many threads running already
+ * @return  -EEXIST, if IPv6 was already initialized.
+ */
+kernel_pid_t ng_ipv6_init(void);
+
+/**
+ * @brief   Demultiplexes a packet according to @p nh.
+ *
+ * @internal
+ *
+ * **Do not use outside this module or its submodules!!!**
+ * Public access needed for Extension Headers.
+ *
+ * @param[in] iface     The receiving interface.
+ * @param[in] pkt       A packet.
+ * @param[in] nh        A protocol number (see @ref net_ng_protnum).
+ */
+void ng_ipv6_demux(kernel_pid_t iface, ng_pktsnip_t *pkt, uint8_t nh);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/sys/net/network_layer/ng_ipv6/Makefile b/sys/net/network_layer/ng_ipv6/Makefile
new file mode 100644
index 0000000000..48422e909a
--- /dev/null
+++ b/sys/net/network_layer/ng_ipv6/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/net/network_layer/ng_ipv6/ng_ipv6.c b/sys/net/network_layer/ng_ipv6/ng_ipv6.c
new file mode 100644
index 0000000000..b53d5d3b2c
--- /dev/null
+++ b/sys/net/network_layer/ng_ipv6/ng_ipv6.c
@@ -0,0 +1,525 @@
+/*
+ * 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 <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "byteorder.h"
+#include "cpu-conf.h"
+#include "kernel_types.h"
+#include "net/ng_netbase.h"
+#include "net/ng_protnum.h"
+#include "thread.h"
+#include "utlist.h"
+
+#include "net/ng_ipv6/nc.h"
+#include "net/ng_ipv6/netif.h"
+
+#include "net/ng_ipv6.h"
+
+#define ENABLE_DEBUG    (0)
+#include "debug.h"
+
+#define _MAX_L2_ADDR_LEN    (8U)
+
+static char _stack[NG_IPV6_STACK_SIZE];
+static kernel_pid_t _pid = KERNEL_PID_UNDEF;
+
+#if ENABLE_DEBUG
+static char addr_str[NG_IPV6_ADDR_MAX_STR_LEN];
+#endif
+
+/* handles NG_NETAPI_MSG_TYPE_RCV commands */
+static void _receive(ng_pktsnip_t *pkt);
+/* dispatches received IPv6 packet for upper layer */
+static void _dispatch_rcv_pkt(ng_nettype_t type, uint32_t demux_ctx,
+                              ng_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 */
+static void _send(ng_pktsnip_t *pkt, bool prep_hdr);
+/* Main event loop for IPv6 */
+static void *_event_loop(void *args);
+
+kernel_pid_t ng_ipv6_init(void)
+{
+    if (_pid == KERNEL_PID_UNDEF) {
+        _pid = thread_create(_stack, NG_IPV6_STACK_SIZE, NG_IPV6_PRIO,
+                             CREATE_STACKTEST, _event_loop, NULL, "ipv6");
+    }
+
+    return _pid;
+}
+
+void ng_ipv6_demux(kernel_pid_t iface, ng_pktsnip_t *pkt, uint8_t nh)
+{
+    int receiver_num;
+
+    pkt->type = ng_nettype_from_protnum(nh);
+
+    /* TODO: add ICMPv6 and extension header handling */
+    (void)iface;    /* will be used by that */
+
+    receiver_num = ng_netreg_num(pkt->type, NG_NETREG_DEMUX_CTX_ALL) +
+                   ng_netreg_num(NG_NETTYPE_IPV6, nh);
+
+    if (receiver_num == 0) {
+        DEBUG("ipv6: unable to forward packet as no one is interested in it\n");
+        ng_pktbuf_release(pkt);
+        return;
+    }
+
+    ng_pktbuf_hold(pkt, receiver_num - 1);
+    /* IPv6 is not interested anymore so `- 1` */
+    _dispatch_rcv_pkt(pkt->type, NG_NETREG_DEMUX_CTX_ALL, pkt);
+    _dispatch_rcv_pkt(NG_NETTYPE_IPV6, nh, pkt);
+}
+
+/* internal functions */
+static void *_event_loop(void *args)
+{
+    msg_t msg, reply, msg_q[NG_IPV6_MSG_QUEUE_SIZE];
+    ng_netreg_entry_t me_reg;
+
+    (void)args;
+    msg_init_queue(msg_q, NG_IPV6_MSG_QUEUE_SIZE);
+
+    me_reg.demux_ctx = NG_NETREG_DEMUX_CTX_ALL;
+    me_reg.pid = thread_getpid();
+
+    /* register interest in all IPv6 packets */
+    ng_netreg_register(NG_NETTYPE_IPV6, &me_reg);
+
+    /* preinitialize ACK */
+    reply.type = NG_NETAPI_MSG_TYPE_ACK;
+
+    /* start event loop */
+    while (1) {
+        DEBUG("ipv6: waiting for incoming message.\n");
+        msg_receive(&msg);
+
+        switch (msg.type) {
+            case NG_NETAPI_MSG_TYPE_RCV:
+                DEBUG("ipv6: NG_NETAPI_MSG_TYPE_RCV received\n");
+                _receive((ng_pktsnip_t *)msg.content.ptr);
+                break;
+
+            case NG_NETAPI_MSG_TYPE_SND:
+                DEBUG("ipv6: NG_NETAPI_MSG_TYPE_SND received\n");
+                _send((ng_pktsnip_t *)msg.content.ptr, true);
+                break;
+
+            case NG_NETAPI_MSG_TYPE_GET:
+            case NG_NETAPI_MSG_TYPE_SET:
+                DEBUG("ipv6: reply to unsupported get/set\n");
+                reply.content.value = -ENOTSUP;
+                msg_reply(&msg, &reply);
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    return NULL;
+}
+
+/* functions for sending */
+static void _send_unicast(kernel_pid_t iface, uint8_t *dst_l2addr,
+                          uint16_t dst_l2addr_len, ng_pktsnip_t *pkt)
+{
+    ng_pktsnip_t *netif;
+
+    if (pkt->type == NG_NETTYPE_NETIF) {
+        /* great: someone already added a netif_hdr_t we assume it's wrong
+         * to keep it simple
+         * XXX: alternative would be to check if ng_netif_hdr_t::dst_l2addr_len
+         * is long enough and only then to throw away the header. This causes
+         * to much overhead IMHO */
+        DEBUG("ipv6: removed old interface header\n");
+        pkt = ng_pktbuf_remove_snip(pkt, pkt);
+    }
+
+    DEBUG("ipv6: add to interface header to packet\n");
+    netif = ng_netif_hdr_build(NULL, 0, dst_l2addr, dst_l2addr_len);
+
+    if (netif == NULL) {
+        DEBUG("ipv6: error on interface header allocation, dropping packet\n");
+        ng_pktbuf_release(pkt);
+        return;
+    }
+
+    /* add netif to front of the pkt list */
+    LL_PREPEND(pkt, netif);
+
+    DEBUG("ipv6: send unicast over interface %" PRIkernel_pid "\n", iface);
+    /* and send to interface */
+    ng_netapi_send(iface, pkt);
+}
+
+static int _fill_ipv6_hdr(kernel_pid_t iface, ng_pktsnip_t *ipv6,
+                          ng_pktsnip_t *payload)
+{
+    int res;
+    ng_ipv6_hdr_t *hdr = ipv6->data;
+
+    hdr->len = byteorder_htons(ng_pkt_len(payload));
+    DEBUG("ipv6: set payload length to %zu (network byteorder %04" PRIx16 ")\n",
+          ng_pkt_len(payload), hdr->len.u16);
+
+    /* check if e.g. extension header was not already marked */
+    if (hdr->nh == NG_PROTNUM_RESERVED) {
+        hdr->nh = ng_nettype_to_protnum(payload->type);
+
+        /* if still reserved: mark no next header */
+        if (hdr->nh == NG_PROTNUM_RESERVED) {
+            hdr->nh = NG_PROTNUM_IPV6_NONXT;
+        }
+    }
+
+    DEBUG("ipv6: set next header to %" PRIu8 "\n", hdr->nh);
+
+    if (hdr->hl == 0) {
+        hdr->hl = ng_ipv6_netif_get(iface)->cur_hl;
+    }
+
+    if (ng_ipv6_addr_is_unspecified(&hdr->src)) {
+        ng_ipv6_addr_t *src = ng_ipv6_netif_find_best_src_addr(iface, &hdr->dst);
+
+        if (src != NULL) {
+            DEBUG("ipv6: set packet source to %s\n",
+                  ng_ipv6_addr_to_str(addr_str, src, sizeof(addr_str)));
+            memcpy(&hdr->src, src, sizeof(ng_ipv6_addr_t));
+        }
+
+        /* Otherwise leave unspecified */
+    }
+
+    DEBUG("ipv6: calculate checksum for upper header.\n");
+
+    if (payload->users > 1) {
+        ng_pktsnip_t *ptr = ipv6;
+
+        /* We deal with multiple interfaces here (multicast) => possible
+         * different source addresses => duplication of payload needed */
+        while (ptr != payload) {
+            /* duplicate everything including payload */
+            ptr->next = ng_pktbuf_start_write(ptr->next);
+
+            if (ptr->next == NULL) {
+                DEBUG("ipv6: unable to get write access to payload, drop it\n");
+                return -ENOBUFS;
+            }
+
+            ptr = ptr->next;
+        }
+    }
+
+    if ((res = ng_netreg_calc_csum(payload, ipv6)) < 0) {
+        if (res != -ENOENT) {   /* if there is no checksum we are okay */
+            DEBUG("ipv6: checksum calculation failed.\n");
+            return res;
+        }
+    }
+
+    return 0;
+}
+
+static inline void _send_multicast_over_iface(kernel_pid_t iface, ng_pktsnip_t *pkt,
+                                              ng_pktsnip_t *netif)
+{
+    DEBUG("ipv6: send multicast over interface %" PRIkernel_pid "\n", ifs[i]);
+    /* mark as multicast */
+    ((ng_netif_hdr_t *)netif->data)->flags |= NG_NETIF_HDR_FLAGS_MULTICAST;
+    /* and send to interface */
+    ng_netapi_send(iface, pkt);
+}
+
+static void _send_multicast(kernel_pid_t iface, ng_pktsnip_t *pkt,
+                            ng_pktsnip_t *ipv6, ng_pktsnip_t *payload,
+                            bool prep_hdr)
+{
+    /* netif header not present: send over all interfaces */
+    if (iface == KERNEL_PID_UNDEF) {
+        size_t ifnum;
+        /* get list of interfaces */
+        kernel_pid_t *ifs = ng_netif_get(&ifnum);
+
+        /* throw away packet if no one is interested */
+        if (ifnum == 0) {
+            DEBUG("ipv6: no interfaces registered, dropping packet\n");
+            ng_pktbuf_release(pkt);
+            return;
+        }
+
+        /* send packet to link layer */
+        ng_pktbuf_hold(pkt, ifnum - 1);
+
+        for (size_t i = 0; i < ifnum; i++) {
+            ng_pktsnip_t *netif;
+
+            if (prep_hdr) {
+                /* need to get second write access (duplication) to fill IPv6
+                 * header interface-local */
+                ipv6 = ng_pktbuf_start_write(ipv6);
+
+                if (ipv6 == NULL) {
+                    DEBUG("ipv6: unable to get write access to IPv6 header, "
+                          "for interface %" PRIkernel_pid "\n", ifs[i]);
+                    ng_pktbuf_release(pkt);
+                    return;
+                }
+
+                if (_fill_ipv6_hdr(ifs[i], ipv6, payload) < 0) {
+                    /* error on filling up header */
+                    ng_pktbuf_release(pkt);
+                    return;
+                }
+            }
+
+            /* allocate interface header */
+            netif = ng_netif_hdr_build(NULL, 0, NULL, 0);
+
+            if (netif == NULL) {
+                DEBUG("ipv6: error on interface header allocation, "
+                      "dropping packet\n");
+                ng_pktbuf_release(pkt);
+                return;
+            }
+
+            LL_PREPEND(pkt, netif);
+
+            _send_multicast_over_iface(iface, pkt, netif);
+        }
+    }
+    else {
+        /* iface != KERNEL_PID_UNDEF implies that netif header is present */
+        if (prep_hdr) {
+            if (_fill_ipv6_hdr(iface, ipv6, payload) < 0) {
+                /* error on filling up header */
+                ng_pktbuf_release(pkt);
+                return;
+            }
+        }
+
+        _send_multicast_over_iface(iface, pkt, netif);
+    }
+}
+
+static void _send(ng_pktsnip_t *pkt, bool prep_hdr)
+{
+    kernel_pid_t iface = KERNEL_PID_UNDEF;
+    ng_pktsnip_t *ipv6, *payload;
+    ng_ipv6_hdr_t *hdr;
+    ng_ipv6_nc_t *nc_entry;
+
+    /* seize payload as temporary variable */
+    payload = ng_pktbuf_start_write(pkt);
+
+    if (payload == NULL) {
+        DEBUG("ipv6: unable to get write access to packet, dropping packet\n");
+        ng_pktbuf_release(pkt);
+        return;
+    }
+
+    pkt = payload;  /* Reset pkt from temporary variable */
+
+    /* get IPv6 snip and (if present) generic interface header */
+    if (pkt->type == NG_NETTYPE_NETIF) {
+        /* If there is already a netif header (routing protocols and
+         * neighbor discovery might add them to preset sending interface) */
+        iface = ((ng_netif_hdr_t *)pkt->data)->if_pid;
+        ipv6 = pkt->next;
+    }
+    else {
+        ipv6 = pkt;
+    }
+
+    hdr = ipv6->data;
+    payload = ipv6->next;       /* TODO: parse extension headers */
+
+    if (ng_ipv6_addr_is_multicast(&hdr->dst)) {
+        _send_multicast(iface, pkt, ipv6, payload, prep_hdr);
+    }
+    else {
+        ng_ipv6_addr_t *next_hop = NULL;
+
+        next_hop = &hdr->dst;   /* TODO: next hop determination */
+
+        if ((nc_entry = ng_ipv6_nc_get_reachable(iface, next_hop)) == NULL) {
+            DEBUG("ipv6: No link layer address for next_hop %s found.\n",
+                  ng_ipv6_addr_to_str(addr_str, next_hop, sizeof(addr_str)));
+            ng_pktbuf_release(pkt);
+            return;
+        }
+        else {
+            iface = nc_entry->iface;
+        }
+
+        if (iface == KERNEL_PID_UNDEF) {
+            DEBUG("ipv6: no interface for %s registered, dropping packet\n",
+                  ng_ipv6_addr_to_str(addr_str, next_hop, sizeof(addr_str)));
+            ng_pktbuf_release(pkt);
+            return;
+        }
+
+        if (prep_hdr) {
+            if (_fill_ipv6_hdr(iface, ipv6, payload) < 0) {
+                /* error on filling up header */
+                ng_pktbuf_release(pkt);
+                return;
+            }
+        }
+
+        _send_unicast(iface, nc_entry->l2_addr, nc_entry->l2_addr_len, pkt);
+    }
+}
+
+/* functions for receiving */
+static inline bool _pkt_not_for_me(kernel_pid_t *iface, ng_ipv6_hdr_t *hdr)
+{
+    if (*iface == KERNEL_PID_UNDEF) {
+        *iface = ng_ipv6_netif_find_by_addr(NULL, &hdr->dst);
+        return (*iface == KERNEL_PID_UNDEF);
+    }
+    else {
+        return (ng_ipv6_netif_find_addr(*iface, &hdr->dst) == NULL);
+    }
+}
+
+static void _dispatch_rcv_pkt(ng_nettype_t type, uint32_t demux_ctx,
+                              ng_pktsnip_t *pkt)
+{
+    msg_t msg;
+    ng_netreg_entry_t *entry = ng_netreg_lookup(type, demux_ctx);
+
+    msg.type = NG_NETAPI_MSG_TYPE_RCV;
+    msg.content.ptr = (char *)pkt;
+
+    while (entry) {
+        DEBUG("ipv6: Send receive command for %p to %" PRIu16 "\n", (void *)pkt,
+              entry->pid);
+        msg_send(&msg, entry->pid);
+        entry = ng_netreg_getnext(entry);
+    }
+}
+
+static void _receive(ng_pktsnip_t *pkt)
+{
+    kernel_pid_t iface = KERNEL_PID_UNDEF;
+    ng_pktsnip_t *ipv6, *netif;
+    ng_ipv6_hdr_t *hdr;
+
+    LL_SEARCH_SCALAR(pkt, netif, type, NG_NETTYPE_NETIF);
+
+    if (netif != NULL) {
+        iface = ((ng_netif_hdr_t *)netif->data)->if_pid;
+    }
+
+    if ((pkt->next != NULL) && (pkt->next->type == NG_NETTYPE_IPV6) &&
+        (pkt->next->size == sizeof(ng_ipv6_hdr_t))) {
+        /* IP header was already marked. Take it. */
+        ipv6 = pkt->next;
+
+        if (!ng_ipv6_hdr_is_ipv6_hdr(ipv6->data)) {
+            DEBUG("ipv6: Received packet was not IPv6, dropping packet\n");
+            ng_pktbuf_release(pkt);
+            return;
+        }
+    }
+    else {
+        if (!ng_ipv6_hdr_is_ipv6_hdr(pkt->data)) {
+            DEBUG("ipv6: Received packet was not IPv6, dropping packet\n");
+            ng_pktbuf_release(pkt);
+            return;
+        }
+
+        /* seize ipv6 as a temporary variable */
+        ipv6 = ng_pktbuf_start_write(pkt);
+
+        if (ipv6 == NULL) {
+            DEBUG("ipv6: unable to get write access to packet, drop it\n");
+            ng_pktbuf_release(pkt);
+            return;
+        }
+
+        pkt = ipv6;     /* reset pkt from temporary variable */
+
+        ipv6 = ng_pktbuf_add(pkt, pkt->data, sizeof(ng_ipv6_hdr_t),
+                             NG_NETTYPE_IPV6);
+
+        if (ipv6 == NULL) {
+            DEBUG("ipv6: error marking IPv6 header, dropping packet\n");
+            ng_pktbuf_release(pkt);
+            return;
+        }
+    }
+
+    /* extract header */
+    hdr = (ng_ipv6_hdr_t *)ipv6->data;
+
+    DEBUG("ipv6: Received (src = %s, ",
+          ng_ipv6_addr_to_str(addr_str, &(hdr->src), sizeof(addr_str)));
+    DEBUG("dst = %s, next header = %" PRIu8 ", length = %" PRIu16 ")\n",
+          ng_ipv6_addr_to_str(addr_str, &(hdr->dst), sizeof(addr_str)),
+          hdr->nh, byteorder_ntohs(hdr->len));
+
+    if (_pkt_not_for_me(&iface, hdr)) { /* if packet is not for me */
+        DEBUG("ipv6: packet destination not this host\n");
+
+#ifdef MODULE_NG_IPV6_ROUTER    /* only routers redirect */
+        /* redirect to next hop */
+        DEBUG("ipv6: decrement hop limit to %" PRIu8 "\n", hdr->hl - 1);
+
+        /* TODO: check if receiving interface is router */
+        if (--(hdr->hl) > 0) {  /* drop packets that *reach* Hop Limit 0 */
+            ng_pktsnip_t *tmp = pkt;
+
+            DEBUG("ipv6: forward packet to next hop\n");
+
+            /* pkt might not be writable yet, if header was given above */
+            pkt = ng_pktbuf_start_write(tmp);
+            ipv6 = ng_pktbuf_start_write(ipv6);
+
+            if ((ipv6 == NULL) || (pkt == NULL)) {
+                DEBUG("ipv6: unable to get write access to packet: dropping it\n");
+                ng_pktbuf_release(tmp);
+                return;
+            }
+
+            ng_pktbuf_release(ipv6->next);  /* remove headers around IPV6 */
+            ipv6->next = pkt;           /* reorder for sending */
+            pkt->next = NULL;
+            _send(ipv6, false);
+        }
+        else {
+            DEBUG("ipv6: hop limit reached 0: drop packet\n");
+            ng_pktbuf_release(pkt);
+            return;
+        }
+
+#else  /* MODULE_NG_IPV6_ROUTER */
+        DEBUG("ipv6: dropping packet\n");
+        /* non rounting hosts just drop the packet */
+        ng_pktbuf_release(pkt);
+        return;
+#endif /* MODULE_NG_IPV6_ROUTER */
+    }
+
+    /* IPv6 internal demuxing (ICMPv6, Extension headers etc.) */
+    ng_ipv6_demux(iface, pkt, hdr->nh);
+}
+
+
+/** @} */
-- 
GitLab