From 65be1eff329cdc4c5f3444f392a1e141ee491359 Mon Sep 17 00:00:00 2001
From: goldsimon <goldsimon@gmx.de>
Date: Tue, 23 Jan 2018 12:36:23 +0100
Subject: [PATCH] ip4/ip6: prevent "protocol unreachable" after raw pcb match
 (!eaten)

Signed-off-by: goldsimon <goldsimon@gmx.de>
---
 src/core/ipv4/ip4.c    | 36 ++++++++++++++++++++++++------------
 src/core/ipv6/ip6.c    | 33 ++++++++++++++++++++++-----------
 src/core/raw.c         | 10 ++++++----
 src/include/lwip/raw.h |  4 ++++
 4 files changed, 56 insertions(+), 27 deletions(-)

diff --git a/src/core/ipv4/ip4.c b/src/core/ipv4/ip4.c
index 3e2ad2b6..6ff06c97 100644
--- a/src/core/ipv4/ip4.c
+++ b/src/core/ipv4/ip4.c
@@ -423,6 +423,9 @@ ip4_input(struct pbuf *p, struct netif *inp)
 #if IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP
   int check_ip_src = 1;
 #endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP */
+#if LWIP_RAW
+  u8_t raw_status;
+#endif /* LWIP_RAW */
 
   LWIP_ASSERT_CORE_LOCKED();
 
@@ -675,7 +678,8 @@ ip4_input(struct pbuf *p, struct netif *inp)
 
 #if LWIP_RAW
   /* raw input did not eat the packet? */
-  if (raw_input(p, inp) == 0)
+  raw_status = raw_input(p, inp);
+  if (raw_status != RAW_INPUT_EATEN)
 #endif /* LWIP_RAW */
   {
     pbuf_remove_header(p, iphdr_hlen); /* Move to payload, no check necessary. */
@@ -708,21 +712,29 @@ ip4_input(struct pbuf *p, struct netif *inp)
         break;
 #endif /* LWIP_IGMP */
       default:
+#if LWIP_RAW
+        if (raw_status == RAW_INPUT_DELIVERED) {
+          MIB2_STATS_INC(mib2.ipindelivers);
+        } else
+#endif /* LWIP_RAW */
+        {
 #if LWIP_ICMP
-        /* send ICMP destination protocol unreachable unless is was a broadcast */
-        if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), netif) &&
-            !ip4_addr_ismulticast(ip4_current_dest_addr())) {
-          pbuf_header_force(p, (s16_t)iphdr_hlen); /* Move to ip header, no check necessary. */
-          icmp_dest_unreach(p, ICMP_DUR_PROTO);
-        }
+          /* send ICMP destination protocol unreachable unless is was a broadcast */
+          if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), netif) &&
+              !ip4_addr_ismulticast(ip4_current_dest_addr())) {
+            pbuf_header_force(p, (s16_t)iphdr_hlen); /* Move to ip header, no check necessary. */
+            icmp_dest_unreach(p, ICMP_DUR_PROTO);
+          }
 #endif /* LWIP_ICMP */
-        pbuf_free(p);
 
-        LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", (u16_t)IPH_PROTO(iphdr)));
+          LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", (u16_t)IPH_PROTO(iphdr)));
 
-        IP_STATS_INC(ip.proterr);
-        IP_STATS_INC(ip.drop);
-        MIB2_STATS_INC(mib2.ipinunknownprotos);
+          IP_STATS_INC(ip.proterr);
+          IP_STATS_INC(ip.drop);
+          MIB2_STATS_INC(mib2.ipinunknownprotos);
+        }
+        pbuf_free(p);
+        break;
     }
   }
 
diff --git a/src/core/ipv6/ip6.c b/src/core/ipv6/ip6.c
index fa4f6eac..04ccec92 100644
--- a/src/core/ipv6/ip6.c
+++ b/src/core/ipv6/ip6.c
@@ -516,6 +516,9 @@ ip6_input(struct pbuf *p, struct netif *inp)
   @todo
   int check_ip_src=1;
 #endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+#if LWIP_RAW
+  u8_t raw_status;
+#endif /* LWIP_RAW */
 
   LWIP_ASSERT_CORE_LOCKED();
 
@@ -1055,7 +1058,8 @@ options_done:
   /* p points to IPv6 header again for raw_input. */
   pbuf_header_force(p, (s16_t)hlen_tot);
   /* raw input did not eat the packet? */
-  if (raw_input(p, inp) == 0)
+  raw_status = raw_input(p, inp);
+  if (raw_status != RAW_INPUT_EATEN)
   {
     /* Point to payload. */
     pbuf_remove_header(p, hlen_tot);
@@ -1085,19 +1089,26 @@ options_done:
       break;
 #endif /* LWIP_ICMP */
     default:
+#if LWIP_RAW
+        if (raw_status == RAW_INPUT_DELIVERED) {
+          /* @todo: ipv6 mib in-delivers? */
+        } else
+#endif /* LWIP_RAW */
+        {
 #if LWIP_ICMP6
-      /* p points to IPv6 header again for raw_input. */
-      pbuf_header_force(p, (s16_t)hlen_tot);
-      /* send ICMP parameter problem unless it was a multicast or ICMPv6 */
-      if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) &&
-          (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) {
-        icmp6_param_problem(p, ICMP6_PP_HEADER, nexth);
-      }
+        /* p points to IPv6 header again for raw_input. */
+        pbuf_header_force(p, (s16_t)hlen_tot);
+        /* send ICMP parameter problem unless it was a multicast or ICMPv6 */
+        if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) &&
+            (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) {
+          icmp6_param_problem(p, ICMP6_PP_HEADER, nexth);
+        }
 #endif /* LWIP_ICMP */
-      LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", (u16_t)IP6H_NEXTH(ip6hdr)));
+        LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", (u16_t)IP6H_NEXTH(ip6hdr)));
+        IP6_STATS_INC(ip6.proterr);
+        IP6_STATS_INC(ip6.drop);
+      }
       pbuf_free(p);
-      IP6_STATS_INC(ip6.proterr);
-      IP6_STATS_INC(ip6.drop);
       break;
     }
   }
diff --git a/src/core/raw.c b/src/core/raw.c
index ae5e339b..6308b858 100644
--- a/src/core/raw.c
+++ b/src/core/raw.c
@@ -135,7 +135,7 @@ raw_input(struct pbuf *p, struct netif *inp)
 {
   struct raw_pcb *pcb, *prev;
   s16_t proto;
-  u8_t eaten = 0;
+  u8_t ret = RAW_INPUT_NONE;
   u8_t broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
 
   LWIP_UNUSED_ARG(inp);
@@ -162,21 +162,22 @@ raw_input(struct pbuf *p, struct netif *inp)
   pcb = raw_pcbs;
   /* loop through all raw pcbs until the packet is eaten by one */
   /* this allows multiple pcbs to match against the packet by design */
-  while ((eaten == 0) && (pcb != NULL)) {
+  while (pcb != NULL) {
     if ((pcb->protocol == proto) && raw_input_local_match(pcb, broadcast) &&
         (((pcb->flags & RAW_FLAGS_CONNECTED) == 0) ||
          ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) {
       /* receive callback function available? */
       if (pcb->recv != NULL) {
+        u8_t eaten;
 #ifndef LWIP_NOASSERT
         void *old_payload = p->payload;
 #endif
+        ret = RAW_INPUT_DELIVERED;
         /* the receive callback function did not eat the packet? */
         eaten = pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr());
         if (eaten != 0) {
           /* receive function ate the packet */
           p = NULL;
-          eaten = 1;
           if (prev != NULL) {
             /* move the pcb to the front of raw_pcbs so that is
                found faster next time */
@@ -184,6 +185,7 @@ raw_input(struct pbuf *p, struct netif *inp)
             pcb->next = raw_pcbs;
             raw_pcbs = pcb;
           }
+          return RAW_INPUT_EATEN;
         } else {
           /* sanity-check that the receive callback did not alter the pbuf */
           LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet",
@@ -196,7 +198,7 @@ raw_input(struct pbuf *p, struct netif *inp)
     prev = pcb;
     pcb = pcb->next;
   }
-  return eaten;
+  return ret;
 }
 
 /**
diff --git a/src/include/lwip/raw.h b/src/include/lwip/raw.h
index 8a872a55..827337e9 100644
--- a/src/include/lwip/raw.h
+++ b/src/include/lwip/raw.h
@@ -52,6 +52,10 @@
 extern "C" {
 #endif
 
+#define RAW_INPUT_NONE           0
+#define RAW_INPUT_EATEN          1
+#define RAW_INPUT_DELIVERED      0xFF
+
 #define RAW_FLAGS_CONNECTED      0x01U
 #define RAW_FLAGS_HDRINCL        0x02U
 #define RAW_FLAGS_MULTICAST_LOOP 0x04U
-- 
2.12.2.windows.2

