From 591ef1826f55cf5f6a07605fcf7ee463b7831828 Mon Sep 17 00:00:00 2001
From: Martine Lenders <mlenders@inf.fu-berlin.de>
Date: Sat, 7 Mar 2015 20:37:45 +0100
Subject: [PATCH] ng_icmpv6: Initial import

---
 Makefile.dep                                |   5 +
 sys/Makefile                                |   3 +
 sys/include/net/ng_icmpv6.h                 | 101 ++++++++++++
 sys/include/net/ng_icmpv6/types.h           |  70 ++++++++
 sys/net/crosslayer/ng_netreg/ng_netreg.c    |   1 +
 sys/net/network_layer/ng_icmpv6/Makefile    |   1 +
 sys/net/network_layer/ng_icmpv6/ng_icmpv6.c | 172 ++++++++++++++++++++
 sys/net/network_layer/ng_ipv6/ng_ipv6.c     |  11 +-
 8 files changed, 362 insertions(+), 2 deletions(-)
 create mode 100644 sys/include/net/ng_icmpv6.h
 create mode 100644 sys/include/net/ng_icmpv6/types.h
 create mode 100644 sys/net/network_layer/ng_icmpv6/Makefile
 create mode 100644 sys/net/network_layer/ng_icmpv6/ng_icmpv6.c

diff --git a/Makefile.dep b/Makefile.dep
index 61498f9d8e..3fe29f8597 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -53,6 +53,10 @@ ifneq (,$(filter ng_sixlowpan_ctx,$(USEMODULE)))
   USEMODULE += vtimer
 endif
 
+ifneq (,$(filter ng_icmpv6,$(USEMODULE)))
+  USEMODULE += ng_ipv6
+endif
+
 ifneq (,$(filter ng_ipv6_hdr,$(USEMODULE)))
   USEMODULE += ng_inet_csum
   USEMODULE += ng_pktbuf
@@ -63,6 +67,7 @@ ifneq (,$(filter ng_ipv6_router,$(USEMODULE)))
 endif
 
 ifneq (,$(filter ng_ipv6,$(USEMODULE)))
+  USEMODULE += ng_icmpv6
   USEMODULE += ng_inet_csum
   USEMODULE += ng_ipv6_addr
   USEMODULE += ng_ipv6_hdr
diff --git a/sys/Makefile b/sys/Makefile
index 2f3500c967..3ec57b53d6 100644
--- a/sys/Makefile
+++ b/sys/Makefile
@@ -62,6 +62,9 @@ endif
 ifneq (,$(filter oneway_malloc,$(USEMODULE)))
     DIRS += oneway-malloc
 endif
+ifneq (,$(filter ng_icmpv6,$(USEMODULE)))
+    DIRS += net/network_layer/ng_icmpv6
+endif
 ifneq (,$(filter ng_ipv6,$(USEMODULE)))
     DIRS += net/network_layer/ng_ipv6
 endif
diff --git a/sys/include/net/ng_icmpv6.h b/sys/include/net/ng_icmpv6.h
new file mode 100644
index 0000000000..f492103631
--- /dev/null
+++ b/sys/include/net/ng_icmpv6.h
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+/**
+ * @defgroup    net_ng_icmpv6  Internet Control Message Protocol for IPv6
+ * @ingroup     net_ng_ipv6
+ * @brief       Basic implementation of ICMPv6
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc4443">
+ *          RFC 4443
+ *      </a>
+ * @{
+ *
+ * @file
+ * @brief       Definitions for ICMPv6
+ *
+ * @author      Martine Lenders <mlenders@inf.fu-berlin.de>
+ */
+
+#ifndef NG_ICMPV6_H_
+#define NG_ICMPV6_H_
+
+#include <inttypes.h>
+
+#include "byteorder.h"
+#include "kernel_types.h"
+#include "net/ng_ipv6/hdr.h"
+#include "net/ng_nettype.h"
+#include "net/ng_nettype.h"
+#include "net/ng_pkt.h"
+
+#include "net/ng_icmpv6/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   General ICMPv6 message format.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc4443#section-2.1">
+ *          RFC 4443, section 2.1
+ *      </a>
+ */
+typedef struct __attribute__((packed)) {
+    uint8_t type;           /**< message type */
+    uint8_t code;           /**< message code */
+    network_uint16_t csum;  /**< checksum */
+} ng_icmpv6_hdr_t;
+
+/**
+ * @brief   Demultiplexes a received ICMPv6 packet according to its type field.
+ *
+ * @param[in] iface     The receiving interface
+ * @param[in] pkt       The packet to demultiplex.
+ */
+void ng_icmpv6_demux(kernel_pid_t iface, ng_pktsnip_t *pkt);
+
+/**
+ * @brief   Builds an ICMPv6 message for sending.
+ *
+ * @param[in] type  Type for the ICMPv6 message.
+ * @param[in] code  Code for the ICMPv6 message.
+ * @param[in] size  Size of the ICMPv6 message (needs do be >
+ *                  `sizeof(ng_icmpv6_hdr_t)`).
+ *
+ * @return  The ICMPv6 message on success
+ * @return  NULL, on failure
+ */
+ng_pktsnip_t *ng_icmpv6_build(uint8_t type, uint8_t code, size_t size);
+
+/* TODO: build error messages */
+
+/**
+ * @brief   Calculates the checksum for an ICMPv6 packet.
+ *
+ * @param[in] hdr           The header the checksum should be calculated
+ *                          for.
+ * @param[in] pseudo_hdr    The header the pseudo header shall be generated
+ *                          from. NULL if none is needed.
+ *
+ * @return  0, on success.
+ * @return  -EINVAL, if ng_pktsnip_t::type of @p pkt was not NG_NETTYPE_ICMPV6
+ * @return  -ENOENT, if ng_pktsnip_t::type of @p pseudo_hdr was not
+ *          NG_NETTYPE_IPV6
+ */
+int ng_icmpv6_calc_csum(ng_pktsnip_t *hdr, ng_pktsnip_t *pseudo_hdr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NG_ICMPV6_H_ */
+/**
+ * @}
+ */
diff --git a/sys/include/net/ng_icmpv6/types.h b/sys/include/net/ng_icmpv6/types.h
new file mode 100644
index 0000000000..fddac3d4f6
--- /dev/null
+++ b/sys/include/net/ng_icmpv6/types.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+/**
+ * @defgroup    net_ng_icmpv6_types ICMPv6 message type definitions
+ * @ingroup     net_ng_icmpv6
+ * @brief       Type numbers for the corresponding field in ICMPv6 messages.
+ * @{
+ *
+ * @file
+ * @brief   Macro definitions for ICMPv6 type fields
+ *
+ * @author  Martine Lenders <mlenders@inf.fu-berlin.de>
+ */
+#ifndef NG_ICMPV6_TYPES_H_
+#define NG_ICMPV6_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @{
+ * @name Error message types
+ * @see <a href="http://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-2">
+ *          IANA, ICMPv6 "type" Numbers
+ *      </a>
+ */
+#define NG_ICMPV6_DEST_UNR      (1)     /**< Destination unreachable message */
+#define NG_ICMPV6_PKT_TOO_BIG   (2)     /**< Packet Too Big message */
+#define NG_ICMPV6_TIME_EXC      (3)     /**< Time Exceeded message */
+#define NG_ICMPV6_PARAM_PROB    (4)     /**< Parameter Problem message */
+#define NG_ICMPV6_ERR_EXP1      (100)   /**< message type for private experimentation */
+#define NG_ICMPV6_ERR_EXP2      (101)   /**< message type for private experimentation */
+/**
+ * @}
+ */
+
+/**
+ * @{
+ * @name Informational message types
+ * @see <a href="http://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-2">
+ *          IANA, ICMPv6 "type" Numbers
+ *      </a>
+ */
+#define NG_ICMPV6_ECHO_REQ      (128)   /**< Echo request message (ping) */
+#define NG_ICMPV6_ECHO_REP      (129)   /**< Echo reply message (pong) */
+#define NG_ICMPV6_RTR_SOL       (133)   /**< NDP router solicitation message */
+#define NG_ICMPV6_RTR_ADV       (134)   /**< NDP router advertisement message */
+#define NG_ICMPV6_NBR_SOL       (135)   /**< NDP neighbor solicitation message */
+#define NG_ICMPV6_NBR_ADV       (136)   /**< NDP neighbor advertisement message */
+#define NG_ICMPV6_REDIRECT      (137)   /**< NDP redirect message */
+#define NG_ICMPV6_RPL_CTRL      (155)   /**< RPL control message */
+#define NG_ICMPV6_INF_EXP1      (200)   /**< message type for private experimentation */
+#define NG_ICMPV6_INF_EXP2      (201)   /**< message type for private experimentation */
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NG_ICMPV6_TYPES_H_ */
+/** @} */
diff --git a/sys/net/crosslayer/ng_netreg/ng_netreg.c b/sys/net/crosslayer/ng_netreg/ng_netreg.c
index 61d05806e9..b7b4239598 100644
--- a/sys/net/crosslayer/ng_netreg/ng_netreg.c
+++ b/sys/net/crosslayer/ng_netreg/ng_netreg.c
@@ -20,6 +20,7 @@
 #include "net/ng_netreg.h"
 #include "net/ng_nettype.h"
 #include "net/ng_pkt.h"
+#include "net/ng_icmpv6.h"
 #include "net/ng_ipv6.h"
 
 #define _INVALID_TYPE(type) (((type) < NG_NETTYPE_UNDEF) || ((type) >= NG_NETTYPE_NUMOF))
diff --git a/sys/net/network_layer/ng_icmpv6/Makefile b/sys/net/network_layer/ng_icmpv6/Makefile
new file mode 100644
index 0000000000..48422e909a
--- /dev/null
+++ b/sys/net/network_layer/ng_icmpv6/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/net/network_layer/ng_icmpv6/ng_icmpv6.c b/sys/net/network_layer/ng_icmpv6/ng_icmpv6.c
new file mode 100644
index 0000000000..f75e57b5aa
--- /dev/null
+++ b/sys/net/network_layer/ng_icmpv6/ng_icmpv6.c
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+/**
+ * @ingroup     net_ng_icmpv6
+ * @{
+ *
+ * @file
+ *
+ * @author      Martine Lenders <mlenders@inf.fu-berlin.de>
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include "byteorder.h"
+#include "kernel_types.h"
+#include "net/ng_netbase.h"
+#include "net/ng_protnum.h"
+#include "net/ng_ipv6/hdr.h"
+#include "od.h"
+#include "utlist.h"
+
+#include "net/ng_icmpv6.h"
+
+#define ENABLE_DEBUG    (0)
+#include "debug.h"
+
+static inline uint16_t _calc_csum(ng_pktsnip_t *hdr,
+                                  ng_pktsnip_t *pseudo_hdr,
+                                  ng_pktsnip_t *payload)
+{
+    uint16_t csum = 0;
+    uint16_t len = (uint16_t)hdr->size;
+
+    while (payload && (payload != hdr)) {
+        csum = ng_inet_csum(csum, payload->data, payload->size);
+        len += (uint16_t)payload->size;
+        payload = payload->next;
+    }
+
+    csum = ng_inet_csum(csum, hdr->data, hdr->size);
+    csum = ng_ipv6_hdr_inet_csum(csum, pseudo_hdr->data, NG_PROTNUM_ICMPV6,
+                                 len);
+
+    return ~csum;
+}
+
+void ng_icmpv6_demux(kernel_pid_t iface, ng_pktsnip_t *pkt)
+{
+    ng_pktsnip_t *icmpv6, *ipv6;
+    ng_icmpv6_hdr_t *hdr;
+    ng_netreg_entry_t *sendto;
+
+    LL_SEARCH_SCALAR(pkt, icmpv6, type, NG_NETTYPE_ICMPV6);
+
+    /* there can be extension headers between IPv6 and ICMPv6 header so we have
+     * to search it */
+    LL_SEARCH_SCALAR(icmpv6, ipv6, type, NG_NETTYPE_IPV6);
+
+    hdr = (ng_icmpv6_hdr_t *)icmpv6->data;
+
+    if (_calc_csum(icmpv6, ipv6, pkt)) {
+        DEBUG("icmpv6: wrong checksum.\n");
+        /* don't release: IPv6 does this */
+        return;
+    }
+
+    switch (hdr->type) {
+        /* TODO: handle ICMPv6 errors */
+#ifdef MODULE_NG_ICMPV6_ECHO
+        case NG_ICMPV6_ECHO_REQ:
+            DEBUG("icmpv6: handle echo request.\n");
+            /* TODO */
+            break;
+#endif
+
+#ifdef MODULE_NG_NDP
+        case NG_ICMPV6_RTR_SOL:
+        case NG_ICMPV6_RTR_ADV:
+        case NG_ICMPV6_NBR_SOL:
+        case NG_ICMPV6_NBR_ADV:
+        case NG_ICMPV6_REDIRECT:
+            DEBUG("icmpv6: neighbor discovery message received\n");
+            /* TODO */
+            break;
+#endif
+
+#ifdef MODULE_NG_RPL
+        case NG_ICMPV6_RPL_CTRL:
+            DEBUG("icmpv6: RPL control message received\n");
+            /* TODO */
+            break;
+#endif
+
+        default:
+            DEBUG("icmpv6: unknown type field %" PRIu8 "\n", hdr->type);
+            break;
+    }
+
+    /* ICMPv6-all will be send in ng_ipv6.c so only dispatch of subtypes is
+     * needed */
+
+    sendto = ng_netreg_lookup(NG_NETTYPE_ICMPV6, hdr->type);
+
+    if (sendto == NULL) {
+        DEBUG("icmpv6: no receivers for ICMPv6 type %" PRIu8 "\n", hdr->type);
+        /* don't release: IPv6 does this */
+        return;
+    }
+
+    /* ICMPv6 is not interested anymore so `- 1` */
+    ng_pktbuf_hold(pkt, ng_netreg_num(NG_NETTYPE_ICMPV6, hdr->type) - 1);
+
+    while (sendto != NULL) {
+        ng_netapi_receive(sendto->pid, pkt);
+        sendto = ng_netreg_getnext(sendto);
+    }
+}
+
+ng_pktsnip_t *ng_icmpv6_build(uint8_t type, uint8_t code, size_t size)
+{
+    ng_pktsnip_t *pkt;
+    ng_icmpv6_hdr_t *icmpv6;
+
+    pkt = ng_pktbuf_add(NULL, NULL, size, NG_NETTYPE_ICMPV6);
+
+    if (pkt == NULL) {
+        DEBUG("icmpv6_echo: no space left in packet buffer\n");
+        return NULL;
+    }
+
+    DEBUG("icmpv6: Building ICMPv6 message with type=%" PRIu8 ", code=%" PRIu8 "\n",
+          type, code);
+    icmpv6 = (ng_icmpv6_hdr_t *)pkt->data;
+    icmpv6->type = type;
+    icmpv6->code = code;
+
+    return pkt;
+}
+
+int ng_icmpv6_calc_csum(ng_pktsnip_t *hdr, ng_pktsnip_t *pseudo_hdr)
+{
+    uint32_t csum = 0;
+
+    if (hdr == NULL) {
+        return -EFAULT;
+    }
+    if (hdr->type != NG_NETTYPE_ICMPV6) {
+        return -EBADMSG;
+    }
+
+    csum = _calc_csum(hdr, pseudo_hdr, hdr->next);
+
+    if (csum == 0) {
+        return -ENOENT;
+    }
+
+    ((ng_icmpv6_hdr_t *)hdr->data)->csum = byteorder_htons(csum);
+
+    return 0;
+}
+
+/**
+ * @}
+ */
diff --git a/sys/net/network_layer/ng_ipv6/ng_ipv6.c b/sys/net/network_layer/ng_ipv6/ng_ipv6.c
index b53d5d3b2c..eb59ae0664 100644
--- a/sys/net/network_layer/ng_ipv6/ng_ipv6.c
+++ b/sys/net/network_layer/ng_ipv6/ng_ipv6.c
@@ -18,6 +18,7 @@
 #include "byteorder.h"
 #include "cpu-conf.h"
 #include "kernel_types.h"
+#include "net/ng_icmpv6.h"
 #include "net/ng_netbase.h"
 #include "net/ng_protnum.h"
 #include "thread.h"
@@ -68,8 +69,14 @@ void ng_ipv6_demux(kernel_pid_t iface, ng_pktsnip_t *pkt, uint8_t nh)
 
     pkt->type = ng_nettype_from_protnum(nh);
 
-    /* TODO: add ICMPv6 and extension header handling */
-    (void)iface;    /* will be used by that */
+    switch (nh) {
+        case NG_PROTNUM_ICMPV6:
+            ng_icmpv6_demux(iface, pkt);
+            break;
+        /* TODO: add extension header handling */
+        default:
+            break;
+    }
 
     receiver_num = ng_netreg_num(pkt->type, NG_NETREG_DEMUX_CTX_ALL) +
                    ng_netreg_num(NG_NETTYPE_IPV6, nh);
-- 
GitLab