From ef1132384ac684df42a6900e3327f1d62f6f400c Mon Sep 17 00:00:00 2001
From: Martine Lenders <mail@martine-lenders.eu>
Date: Sat, 17 Nov 2018 01:53:10 +0100
Subject: [PATCH] gnrc_icmpv6_error: check IPv6 header before starting to build

Check for:

 - if it exists (critical error condition -- non-IPv6 headers should
   not trigger these functions) => assert
 - if it has a multicast source (that shouldn't really happen but
   people might try weird stuff ;-)
 - if it has an unspecified source (can't determine receiver of error
   message => don't send it, don't build it)
---
 sys/include/net/gnrc/icmpv6/error.h           | 11 ++++
 .../icmpv6/error/gnrc_icmpv6_error.c          | 61 ++++++++++++++-----
 2 files changed, 57 insertions(+), 15 deletions(-)

diff --git a/sys/include/net/gnrc/icmpv6/error.h b/sys/include/net/gnrc/icmpv6/error.h
index 7bd527e131..554fc9c102 100644
--- a/sys/include/net/gnrc/icmpv6/error.h
+++ b/sys/include/net/gnrc/icmpv6/error.h
@@ -39,6 +39,9 @@ extern "C" {
  *
  * @pre @p orig_pkt contains a packet snip of type @ref GNRC_NETTYPE_IPV6
  *
+ * @note    Won't send if source address of @p orig_pkt is unspecified or
+ *          multicast
+ *
  * @param[in] code      The [code for the message](@ref net_icmpv6_error_dst_unr_codes).
  * @param[in] orig_pkt  The invoking packet.
  */
@@ -49,6 +52,9 @@ void gnrc_icmpv6_error_dst_unr_send(uint8_t code, const gnrc_pktsnip_t *orig_pkt
  *
  * @pre @p orig_pkt contains a packet snip of type @ref GNRC_NETTYPE_IPV6
  *
+ * @note    Won't send if source address of @p orig_pkt is unspecified or
+ *          multicast
+ *
  * @param[in] mtu       The maximum transission unit of the next-hop link.
  * @param[in] orig_pkt  The invoking packet.
  */
@@ -60,6 +66,9 @@ void gnrc_icmpv6_error_pkt_too_big_send(uint32_t mtu,
  *
  * @pre @p orig_pkt contains a packet snip of type @ref GNRC_NETTYPE_IPV6
  *
+ * @note    Won't send if source address of @p orig_pkt is unspecified or
+ *          multicast
+ *
  * @param[in] code      The [code for the message](@ref net_icmpv6_error_time_exc_codes).
  * @param[in] orig_pkt  The invoking packet.
  */
@@ -72,6 +81,8 @@ void gnrc_icmpv6_error_time_exc_send(uint8_t code,
  * @pre @p orig_pkt is in receive order.
  * @pre @p orig_pkt contains a packet snip of type @ref GNRC_NETTYPE_IPV6
  *
+ * @note    Won't send if source address of @p orig_pkt is unspecified or
+ *          multicast
  *
  * @param[in] code      The [code for the message](@ref net_icmpv6_error_param_prob_codes).
  * @param[in] ptr       Pointer to the errorneous octet in @p orig_pkt.
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
index ba1e028fb4..cb8b6b588a 100644
--- a/sys/net/gnrc/network_layer/icmpv6/error/gnrc_icmpv6_error.c
+++ b/sys/net/gnrc/network_layer/icmpv6/error/gnrc_icmpv6_error.c
@@ -192,16 +192,16 @@ static gnrc_pktsnip_t *_param_prob_build(uint8_t code, void *ptr,
     return pkt;
 }
 
-static void _send(gnrc_pktsnip_t *pkt, const gnrc_pktsnip_t *orig_pkt)
+static void _send(gnrc_pktsnip_t *pkt, const gnrc_pktsnip_t *orig_pkt,
+                  gnrc_pktsnip_t *ipv6)
 {
     if (pkt != NULL) {
         /* discarding const qualifier is safe here */
-        gnrc_pktsnip_t *ipv6 = gnrc_pktsnip_search_type((gnrc_pktsnip_t *)orig_pkt,
-                                                        GNRC_NETTYPE_IPV6);
         gnrc_pktsnip_t *netif = gnrc_pktsnip_search_type((gnrc_pktsnip_t *)orig_pkt,
                                                          GNRC_NETTYPE_NETIF);
         assert(ipv6 != NULL);
         ipv6_hdr_t *ipv6_hdr = ipv6->data;
+        /* overwrite ipv6 parameter pointer ... we don't need it after this */
         ipv6 = gnrc_ipv6_hdr_build(pkt, NULL, &ipv6_hdr->src);
         if (ipv6 == NULL) {
             DEBUG("gnrc_icmpv6_error: No space in packet buffer left\n");
@@ -237,39 +237,70 @@ static void _send(gnrc_pktsnip_t *pkt, const gnrc_pktsnip_t *orig_pkt)
     }
 }
 
+static gnrc_pktsnip_t *_check_ipv6_hdr(const gnrc_pktsnip_t *orig_pkt)
+{
+    /* discarding const qualifier is safe here */
+    gnrc_pktsnip_t *ipv6 = gnrc_pktsnip_search_type((gnrc_pktsnip_t *)orig_pkt,
+                                                    GNRC_NETTYPE_IPV6);
+    assert(ipv6 != NULL);
+    const ipv6_hdr_t *ipv6_hdr = ipv6->data;
+
+    if (ipv6_addr_is_unspecified(&ipv6_hdr->src) ||
+        ipv6_addr_is_multicast(&ipv6_hdr->src)) {
+        ipv6 = NULL;
+    }
+    return ipv6;
+}
+
 void gnrc_icmpv6_error_dst_unr_send(uint8_t code, const gnrc_pktsnip_t *orig_pkt)
 {
-    gnrc_pktsnip_t *pkt = _dst_unr_build(code, orig_pkt);
+    gnrc_pktsnip_t *ipv6 = _check_ipv6_hdr(orig_pkt);
+
+    if (ipv6 != NULL) {
+        gnrc_pktsnip_t *pkt = _dst_unr_build(code, orig_pkt);
 
-    DEBUG("gnrc_icmpv6_error: trying to send destination unreachable error\n");
-    _send(pkt, orig_pkt);
+        DEBUG("gnrc_icmpv6_error: trying to send destination unreachable error\n");
+        _send(pkt, orig_pkt, ipv6);
+    }
 }
 
 void gnrc_icmpv6_error_pkt_too_big_send(uint32_t mtu,
                                         const gnrc_pktsnip_t *orig_pkt)
 {
-    gnrc_pktsnip_t *pkt = _pkt_too_big_build(mtu, orig_pkt);
+    gnrc_pktsnip_t *ipv6 = _check_ipv6_hdr(orig_pkt);
 
-    DEBUG("gnrc_icmpv6_error: trying to send packet too big error\n");
-    _send(pkt, orig_pkt);
+    if (ipv6 != NULL) {
+        gnrc_pktsnip_t *pkt = _pkt_too_big_build(mtu, orig_pkt);
+
+        DEBUG("gnrc_icmpv6_error: trying to send packet too big error\n");
+        _send(pkt, orig_pkt, ipv6);
+    }
 }
 
 void gnrc_icmpv6_error_time_exc_send(uint8_t code,
                                      const gnrc_pktsnip_t *orig_pkt)
 {
-    gnrc_pktsnip_t *pkt = _time_exc_build(code, orig_pkt);
+    gnrc_pktsnip_t *ipv6 = _check_ipv6_hdr(orig_pkt);
+
+    if (ipv6 != NULL) {
+        gnrc_pktsnip_t *pkt = _time_exc_build(code, orig_pkt);
 
-    DEBUG("gnrc_icmpv6_error: trying to send time exceeded error\n");
-    _send(pkt, orig_pkt);
+        DEBUG("gnrc_icmpv6_error: trying to send time exceeded error\n");
+        _send(pkt, orig_pkt, ipv6);
+    }
 }
 
 void gnrc_icmpv6_error_param_prob_send(uint8_t code, void *ptr,
                                        const gnrc_pktsnip_t *orig_pkt)
 {
-    gnrc_pktsnip_t *pkt = _param_prob_build(code, ptr, orig_pkt);
+    gnrc_pktsnip_t *ipv6 = _check_ipv6_hdr(orig_pkt);
+
+    if (ipv6 != NULL) {
+        gnrc_pktsnip_t *pkt = _param_prob_build(code, ptr, orig_pkt);
 
-    DEBUG("gnrc_icmpv6_error: trying to send parameter problem error\n");
-    _send(pkt, orig_pkt);
+        DEBUG("gnrc_icmpv6_error: trying to send parameter problem error\n");
+        _send(pkt, orig_pkt, ipv6);
+    }
 }
 
 /** @} */
-- 
GitLab