VPP-48 Fixes for ip4/6 ttl checks and icmp responses 54/1654/6
authorChris Luke <chrisy@flirble.org>
Tue, 14 Jun 2016 20:24:47 +0000 (16:24 -0400)
committerDave Barach <openvpp@barachs.net>
Tue, 28 Jun 2016 23:35:13 +0000 (23:35 +0000)
This patch fixes a few minor things:

- Previously ip[46]_input was rejecting packets with an input TTL (hop
  limit) of one; this was not correct behavior. Packets that are bound
  for this device can validly have a TTL of one.
- ip[46]_forward was not generating an ICMP TTL expired message if the
  act of decrementing the TTL caused it to become zero. This was not
  previously an issue because ip[46]_input was filtering packets where
  this could happen.
- udp_local was not generating ICMP Port Unreachable messages if
  UDP packets arrived for a port that is not listened to. This is
  typically the signal that "traceroute" uses to terminate its
  search.

Together these fixes mean that traceroute probes transiting a VPP
node, or are targetted toward a VPP node, now work as expected.

Change-Id: I84bb940883f7a18435f29f4518fb0445b989a3e3
Signed-off-by: Chris Luke <chrisy@flirble.org>
vnet/vnet/ip/icmp4.h
vnet/vnet/ip/ip4_forward.c
vnet/vnet/ip/ip4_input.c
vnet/vnet/ip/ip6_forward.c
vnet/vnet/ip/ip6_input.c
vnet/vnet/ip/udp_local.c

index f99bf2d..07fe52d 100644 (file)
@@ -28,7 +28,7 @@
   _ (DST_LOOKUP_MISS, "icmp6 dst address lookup misses")                \
   _ (DEST_UNREACH_SENT, "destination unreachable response sent")       \
   _ (TTL_EXPIRE_SENT, "hop limit exceeded response sent")              \
-  _ (PARAM_PROBLEM_SENT, "parameter Pproblem response sent")           \
+  _ (PARAM_PROBLEM_SENT, "parameter problem response sent")            \
   _ (DROP, "error message dropped")
 
 typedef enum {
index 96036dc..6008ec2 100644 (file)
@@ -2516,6 +2516,7 @@ ip4_probe_neighbor (vlib_main_t * vm, ip4_address_t * dst, u32 sw_if_index)
 typedef enum {
   IP4_REWRITE_NEXT_DROP,
   IP4_REWRITE_NEXT_ARP,
+  IP4_REWRITE_NEXT_ICMP_ERROR,
 } ip4_rewrite_next_t;
 
 always_inline uword
@@ -2585,6 +2586,7 @@ ip4_rewrite_inline (vlib_main_t * vm,
          ip1 = vlib_buffer_get_current (p1);
 
          error0 = error1 = IP4_ERROR_NONE;
+          next0 = next1 = IP4_REWRITE_NEXT_DROP;
 
          /* Decrement TTL & update checksum.
             Works either endian, so no need for byte swap. */
@@ -2611,8 +2613,26 @@ ip4_rewrite_inline (vlib_main_t * vm,
              ip0->ttl = ttl0;
              ip1->ttl = ttl1;
 
-             error0 = ttl0 <= 0 ? IP4_ERROR_TIME_EXPIRED : error0;
-             error1 = ttl1 <= 0 ? IP4_ERROR_TIME_EXPIRED : error1;
+              /*
+               * If the ttl drops below 1 when forwarding, generate
+               * an ICMP response.
+               */
+              if (PREDICT_FALSE(ttl0 <= 0))
+                {
+                  error0 = IP4_ERROR_TIME_EXPIRED;
+                  vnet_buffer (p0)->sw_if_index[VLIB_TX] = (u32)~0;
+                  icmp4_error_set_vnet_buffer(p0, ICMP4_time_exceeded,
+                              ICMP4_time_exceeded_ttl_exceeded_in_transit, 0);
+                  next0 = IP4_REWRITE_NEXT_ICMP_ERROR;
+                }
+              if (PREDICT_FALSE(ttl1 <= 0))
+                {
+                  error1 = IP4_ERROR_TIME_EXPIRED;
+                  vnet_buffer (p1)->sw_if_index[VLIB_TX] = (u32)~0;
+                  icmp4_error_set_vnet_buffer(p1, ICMP4_time_exceeded,
+                              ICMP4_time_exceeded_ttl_exceeded_in_transit, 0);
+                  next1 = IP4_REWRITE_NEXT_ICMP_ERROR;
+                }
 
              /* Verify checksum. */
              ASSERT (ip0->checksum == ip4_header_checksum (ip0));
@@ -2657,14 +2677,14 @@ ip4_rewrite_inline (vlib_main_t * vm,
                     ? IP4_ERROR_MTU_EXCEEDED
                     : error1);
 
-         next0 = (error0 == IP4_ERROR_NONE) 
-            ? adj0[0].rewrite_header.next_index : 0;
+          next0 = (error0 == IP4_ERROR_NONE)
+            ? adj0[0].rewrite_header.next_index : next0;
 
           if (rewrite_for_locally_received_packets)
               next0 = next0 && next0_override ? next0_override : next0;
 
-         next1 = (error1 == IP4_ERROR_NONE)
-            ? adj1[0].rewrite_header.next_index : 0;
+          next1 = (error1 == IP4_ERROR_NONE)
+            ? adj1[0].rewrite_header.next_index : next1;
 
           if (rewrite_for_locally_received_packets)
               next1 = next1 && next1_override ? next1_override : next1;
@@ -2686,17 +2706,24 @@ ip4_rewrite_inline (vlib_main_t * vm,
                    /* packet increment */ 0,
                    /* byte increment */ rw_len1-sizeof(ethernet_header_t));
 
-         p0->current_data -= rw_len0;
-         p1->current_data -= rw_len1;
-
-         p0->current_length += rw_len0;
-         p1->current_length += rw_len1;
-
-         vnet_buffer (p0)->sw_if_index[VLIB_TX] = adj0[0].rewrite_header.sw_if_index;
-         vnet_buffer (p1)->sw_if_index[VLIB_TX] = adj1[0].rewrite_header.sw_if_index;
-      
-         p0->error = error_node->errors[error0];
-         p1->error = error_node->errors[error1];
+          /* Don't adjust the buffer for ttl issue; icmp-error node wants
+           * to see the IP headerr */
+          if (PREDICT_TRUE(error0 == IP4_ERROR_NONE))
+            {
+              p0->current_data -= rw_len0;
+              p0->current_length += rw_len0;
+              p0->error = error_node->errors[error0];
+              vnet_buffer (p0)->sw_if_index[VLIB_TX] =
+                  adj0[0].rewrite_header.sw_if_index;
+            }
+          if (PREDICT_TRUE(error1 == IP4_ERROR_NONE))
+            {
+              p1->current_data -= rw_len1;
+              p1->current_length += rw_len1;
+              p1->error = error_node->errors[error1];
+              vnet_buffer (p1)->sw_if_index[VLIB_TX] =
+                  adj1[0].rewrite_header.sw_if_index;
+            }
 
          /* Guess we are only writing on simple Ethernet header. */
          vnet_rewrite_two_headers (adj0[0], adj1[0],
@@ -2733,7 +2760,7 @@ ip4_rewrite_inline (vlib_main_t * vm,
          ip0 = vlib_buffer_get_current (p0);
 
          error0 = IP4_ERROR_NONE;
-          next0 = 0;            /* drop on error */
+          next0 = IP4_REWRITE_NEXT_DROP;            /* drop on error */
 
          /* Decrement TTL & update checksum. */
          if (! rewrite_for_locally_received_packets)
@@ -2754,7 +2781,18 @@ ip4_rewrite_inline (vlib_main_t * vm,
 
              ASSERT (ip0->checksum == ip4_header_checksum (ip0));
 
-             error0 = ttl0 <= 0 ? IP4_ERROR_TIME_EXPIRED : error0;
+              if (PREDICT_FALSE(ttl0 <= 0))
+                {
+                  /*
+                   * If the ttl drops below 1 when forwarding, generate
+                   * an ICMP response.
+                   */
+                  error0 = IP4_ERROR_TIME_EXPIRED;
+                  next0 = IP4_REWRITE_NEXT_ICMP_ERROR;
+                  vnet_buffer (p0)->sw_if_index[VLIB_TX] = (u32)~0;
+                  icmp4_error_set_vnet_buffer(p0, ICMP4_time_exceeded,
+                              ICMP4_time_exceeded_ttl_exceeded_in_transit, 0);
+                }
            }
 
           if (rewrite_for_locally_received_packets)
@@ -2796,15 +2834,20 @@ ip4_rewrite_inline (vlib_main_t * vm,
                     > adj0[0].rewrite_header.max_l3_packet_bytes
                     ? IP4_ERROR_MTU_EXCEEDED
                     : error0);
-          
+
          p0->error = error_node->errors[error0];
-          p0->current_data -= rw_len0;
-          p0->current_length += rw_len0;
-          vnet_buffer (p0)->sw_if_index[VLIB_TX] = 
-            adj0[0].rewrite_header.sw_if_index;
-          
-          next0 = (error0 == IP4_ERROR_NONE)
-            ? adj0[0].rewrite_header.next_index : 0;
+
+          /* Don't adjust the buffer for ttl issue; icmp-error node wants
+           * to see the IP headerr */
+          if (PREDICT_TRUE(error0 == IP4_ERROR_NONE))
+            {
+              p0->current_data -= rw_len0;
+              p0->current_length += rw_len0;
+
+              vnet_buffer (p0)->sw_if_index[VLIB_TX] =
+                  adj0[0].rewrite_header.sw_if_index;
+              next0 = adj0[0].rewrite_header.next_index;
+            }
 
           if (rewrite_for_locally_received_packets)
               next0 = next0 && next0_override ? next0_override : next0;
@@ -2854,10 +2897,11 @@ VLIB_REGISTER_NODE (ip4_rewrite_node) = {
 
   .format_trace = format_ip4_rewrite_trace,
 
-  .n_next_nodes = 2,
+  .n_next_nodes = 3,
   .next_nodes = {
     [IP4_REWRITE_NEXT_DROP] = "error-drop",
     [IP4_REWRITE_NEXT_ARP] = "ip4-arp",
+    [IP4_REWRITE_NEXT_ICMP_ERROR] = "ip4-icmp-error",
   },
 };
 
index 5b2b42d..c5281c4 100644 (file)
@@ -194,9 +194,9 @@ ip4_input_inline (vlib_main_t * vm,
          error0 = ip4_get_fragment_offset (ip0) == 1 ? IP4_ERROR_FRAGMENT_OFFSET_ONE : error0;
          error1 = ip4_get_fragment_offset (ip1) == 1 ? IP4_ERROR_FRAGMENT_OFFSET_ONE : error1;
 
-         /* TTL <= 1? Drop it. */
-         error0 = (ip0->ttl <= 1 && cast0 == VNET_UNICAST) ? IP4_ERROR_TIME_EXPIRED : error0;
-         error1 = (ip1->ttl <= 1 && cast1 == VNET_UNICAST) ? IP4_ERROR_TIME_EXPIRED : error1;
+         /* TTL < 1? Drop it. */
+         error0 = (ip0->ttl < 1 && cast0 == VNET_UNICAST) ? IP4_ERROR_TIME_EXPIRED : error0;
+         error1 = (ip1->ttl < 1 && cast1 == VNET_UNICAST) ? IP4_ERROR_TIME_EXPIRED : error1;
 
          /* Verify lengths. */
          ip_len0 = clib_net_to_host_u16 (ip0->length);
@@ -293,8 +293,8 @@ ip4_input_inline (vlib_main_t * vm,
          /* Drop fragmentation offset 1 packets. */
          error0 = ip4_get_fragment_offset (ip0) == 1 ? IP4_ERROR_FRAGMENT_OFFSET_ONE : error0;
 
-         /* TTL <= 1? Drop it. */
-          error0 = (ip0->ttl <= 1 && cast0 == VNET_UNICAST) ? IP4_ERROR_TIME_EXPIRED : error0;
+         /* TTL < 1? Drop it. */
+          error0 = (ip0->ttl < 1 && cast0 == VNET_UNICAST) ? IP4_ERROR_TIME_EXPIRED : error0;
 
          /* Verify lengths. */
          ip_len0 = clib_net_to_host_u16 (ip0->length);
index afb23f1..eabaa89 100644 (file)
@@ -2277,6 +2277,7 @@ ip6_probe_neighbor (vlib_main_t * vm, ip6_address_t * dst, u32 sw_if_index)
 
 typedef enum {
   IP6_REWRITE_NEXT_DROP,
+  IP6_REWRITE_NEXT_ICMP_ERROR,
 } ip6_rewrite_next_t;
 
 always_inline uword
@@ -2345,6 +2346,7 @@ ip6_rewrite_inline (vlib_main_t * vm,
          ip1 = vlib_buffer_get_current (p1);
 
          error0 = error1 = IP6_ERROR_NONE;
+          next0 = next1 = IP6_REWRITE_NEXT_DROP;
 
          if (! rewrite_for_locally_received_packets)
            {
@@ -2360,8 +2362,26 @@ ip6_rewrite_inline (vlib_main_t * vm,
              ip0->hop_limit = hop_limit0;
              ip1->hop_limit = hop_limit1;
 
-             error0 = hop_limit0 <= 0 ? IP6_ERROR_TIME_EXPIRED : error0;
-             error1 = hop_limit1 <= 0 ? IP6_ERROR_TIME_EXPIRED : error1;
+              /*
+               * If the hop count drops below 1 when forwarding, generate
+               * an ICMP response.
+               */
+              if (PREDICT_FALSE(hop_limit0 <= 0))
+                {
+                  error0 = IP6_ERROR_TIME_EXPIRED;
+                  next0 = IP6_REWRITE_NEXT_ICMP_ERROR;
+                  vnet_buffer (p0)->sw_if_index[VLIB_TX] = (u32)~0;
+                  icmp6_error_set_vnet_buffer(p0, ICMP6_time_exceeded,
+                        ICMP6_time_exceeded_ttl_exceeded_in_transit, 0);
+                }
+              if (PREDICT_FALSE(hop_limit1 <= 0))
+                {
+                  error1 = IP6_ERROR_TIME_EXPIRED;
+                  next1 = IP6_REWRITE_NEXT_ICMP_ERROR;
+                  vnet_buffer (p1)->sw_if_index[VLIB_TX] = (u32)~0;
+                  icmp6_error_set_vnet_buffer(p1, ICMP6_time_exceeded,
+                        ICMP6_time_exceeded_ttl_exceeded_in_transit, 0);
+                }
            }
 
          adj0 = ip_get_adjacency (lm, adj_index0);
@@ -2403,19 +2423,26 @@ ip6_rewrite_inline (vlib_main_t * vm,
                    ? IP6_ERROR_MTU_EXCEEDED
                    : error1);
 
-         p0->current_data -= rw_len0;
-         p1->current_data -= rw_len1;
+          /* Don't adjust the buffer for hop count issue; icmp-error node
+           * wants to see the IP headerr */
+          if (PREDICT_TRUE(error0 == IP6_ERROR_NONE))
+            {
+              p0->current_data -= rw_len0;
+              p0->current_length += rw_len0;
 
-         p0->current_length += rw_len0;
-         p1->current_length += rw_len1;
+              vnet_buffer (p0)->sw_if_index[VLIB_TX] =
+                  adj0[0].rewrite_header.sw_if_index;
+              next0 = adj0[0].rewrite_header.next_index;
+            }
+          if (PREDICT_TRUE(error1 == IP6_ERROR_NONE))
+            {
+              p1->current_data -= rw_len1;
+              p1->current_length += rw_len1;
 
-         vnet_buffer (p0)->sw_if_index[VLIB_TX] = adj0[0].rewrite_header.sw_if_index;
-         vnet_buffer (p1)->sw_if_index[VLIB_TX] = adj1[0].rewrite_header.sw_if_index;
-      
-         next0 = (error0 == IP6_ERROR_NONE) ? 
-            adj0[0].rewrite_header.next_index : IP6_REWRITE_NEXT_DROP;
-         next1 = (error1 == IP6_ERROR_NONE) ? 
-            adj1[0].rewrite_header.next_index : IP6_REWRITE_NEXT_DROP;
+             vnet_buffer (p1)->sw_if_index[VLIB_TX] =
+                  adj1[0].rewrite_header.sw_if_index;
+              next1 = adj1[0].rewrite_header.next_index;
+            }
 
          /* Guess we are only writing on simple Ethernet header. */
          vnet_rewrite_two_headers (adj0[0], adj1[0],
@@ -2449,6 +2476,7 @@ ip6_rewrite_inline (vlib_main_t * vm,
          ip0 = vlib_buffer_get_current (p0);
 
          error0 = IP6_ERROR_NONE;
+          next0 = IP6_REWRITE_NEXT_DROP;
 
          /* Check hop limit */
          if (! rewrite_for_locally_received_packets)
@@ -2461,7 +2489,18 @@ ip6_rewrite_inline (vlib_main_t * vm,
 
              ip0->hop_limit = hop_limit0;
 
-             error0 = hop_limit0 <= 0 ? IP6_ERROR_TIME_EXPIRED : error0;
+              if (PREDICT_FALSE(hop_limit0 <= 0))
+                {
+                  /*
+                   * If the hop count drops below 1 when forwarding, generate
+                   * an ICMP response.
+                   */
+                  error0 = IP6_ERROR_TIME_EXPIRED;
+                  next0 = IP6_REWRITE_NEXT_ICMP_ERROR;
+                  vnet_buffer (p0)->sw_if_index[VLIB_TX] = (u32)~0;
+                  icmp6_error_set_vnet_buffer(p0, ICMP6_time_exceeded,
+                        ICMP6_time_exceeded_ttl_exceeded_in_transit, 0);
+                }
            }
 
           if (rewrite_for_locally_received_packets)
@@ -2488,12 +2527,17 @@ ip6_rewrite_inline (vlib_main_t * vm,
                    ? IP6_ERROR_MTU_EXCEEDED
                    : error0);
 
-         p0->current_data -= rw_len0;
-         p0->current_length += rw_len0;
-         vnet_buffer (p0)->sw_if_index[VLIB_TX] = adj0[0].rewrite_header.sw_if_index;
-      
-         next0 = (error0 == IP6_ERROR_NONE) ?
-            adj0[0].rewrite_header.next_index : IP6_REWRITE_NEXT_DROP;
+          /* Don't adjust the buffer for hop count issue; icmp-error node
+           * wants to see the IP headerr */
+          if (PREDICT_TRUE(error0 == IP6_ERROR_NONE))
+            {
+             p0->current_data -= rw_len0;
+             p0->current_length += rw_len0;
+
+             vnet_buffer (p0)->sw_if_index[VLIB_TX] =
+                  adj0[0].rewrite_header.sw_if_index;
+              next0 = adj0[0].rewrite_header.next_index;
+            }
 
          p0->error = error_node->errors[error0];
 
@@ -2542,9 +2586,10 @@ VLIB_REGISTER_NODE (ip6_rewrite_node) = {
 
   .format_trace = format_ip6_rewrite_trace,
 
-  .n_next_nodes = 1,
+  .n_next_nodes = 2,
   .next_nodes = {
     [IP6_REWRITE_NEXT_DROP] = "error-drop",
+    [IP6_REWRITE_NEXT_ICMP_ERROR] = "ip6-icmp-error",
   },
 };
 
index 7b5470d..05929c5 100644 (file)
@@ -177,8 +177,8 @@ ip6_input (vlib_main_t * vm,
            * like dhcpv6 packets from client has hop-limit 1, which should not
            * be dropped.
            */
-         error0 = ip0->hop_limit <= 1 ? IP6_ERROR_TIME_EXPIRED : error0;
-         error1 = ip1->hop_limit <= 1 ? IP6_ERROR_TIME_EXPIRED : error1;
+         error0 = ip0->hop_limit < 1 ? IP6_ERROR_TIME_EXPIRED : error0;
+         error1 = ip1->hop_limit < 1 ? IP6_ERROR_TIME_EXPIRED : error1;
 
          /* L2 length must be at least minimal IP header. */
          error0 = p0->current_length < sizeof (ip0[0]) ? IP6_ERROR_TOO_SHORT : error0;
@@ -252,7 +252,7 @@ ip6_input (vlib_main_t * vm,
            * like dhcpv6 packets from client has hop-limit 1, which should not
            * be dropped.
            */
-         error0 = ip0->hop_limit <= 1 ? IP6_ERROR_TIME_EXPIRED : error0;
+         error0 = ip0->hop_limit < 1 ? IP6_ERROR_TIME_EXPIRED : error0;
 
          /* L2 length must be at least minimal IP header. */
          error0 = p0->current_length < sizeof (ip0[0]) ? IP6_ERROR_TOO_SHORT : error0;
index 354dd4e..9b70147 100644 (file)
 
 udp_main_t udp_main;
 
-#define foreach_udp_input_next                 \
-  _ (PUNT, "error-punt")                       \
-  _ (DROP, "error-drop")
+#define foreach_udp_input_next                  \
+  _ (PUNT, "error-punt")                        \
+  _ (DROP, "error-drop")                        \
+  _ (ICMP4_ERROR, "ip4-icmp-error")             \
+  _ (ICMP6_ERROR, "ip6-icmp-error")
 
 typedef enum {
 #define _(s,n) UDP_INPUT_NEXT_##s,
@@ -37,6 +39,7 @@ typedef enum {
 typedef struct {
   u16 src_port;
   u16 dst_port;
+  u8 bound;
 } udp_rx_trace_t;
 
 u8 * format_udp_rx_trace (u8 * s, va_list * args)
@@ -45,9 +48,10 @@ u8 * format_udp_rx_trace (u8 * s, va_list * args)
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
   udp_rx_trace_t * t = va_arg (*args, udp_rx_trace_t *);
     
-  s = format (s, "UDP: src-port %d dst-port %d",
+  s = format (s, "UDP: src-port %d dst-port %d%s",
               clib_net_to_host_u16(t->src_port),
-              clib_net_to_host_u16(t->dst_port));
+              clib_net_to_host_u16(t->dst_port),
+              t->bound ? "" : " (no listener)");
   return s;
 }
 
@@ -72,6 +76,7 @@ udp46_input_inline (vlib_main_t * vm,
     (void *) vlib_node_get_runtime_data (vm, udp4_input_node.index)
     : (void *) vlib_node_get_runtime_data (vm, udp6_input_node.index);
   __attribute__((unused)) u32 n_left_from, next_index, i_next, * from, * to_next;
+  word n_no_listener = 0;
 
   from = vlib_frame_vector_args (from_frame);
   n_left_from = from_frame->n_vectors;
@@ -177,10 +182,59 @@ udp46_input_inline (vlib_main_t * vm,
           next0 = (error0 == 0) ? vec_elt(rt->next_by_dst_port, i0) : next0;
           next1 = (error1 == 0) ? vec_elt(rt->next_by_dst_port, i1) : next1;
 
-         b0->error = node->errors[next0 == SPARSE_VEC_INVALID_INDEX ? 
-                                  UDP_ERROR_NO_LISTENER : error0];
-         b1->error = node->errors[next1 == SPARSE_VEC_INVALID_INDEX ? 
-                                  UDP_ERROR_NO_LISTENER : error1];
+          if (PREDICT_FALSE(next0 == SPARSE_VEC_INVALID_INDEX))
+            {
+              // move the pointer back so icmp-error can find the
+              // ip packet header
+              vlib_buffer_advance (b0, - (word)advance0);
+
+              if (is_ip4)
+                {
+                  icmp4_error_set_vnet_buffer(b0, ICMP4_destination_unreachable,
+                        ICMP4_destination_unreachable_port_unreachable, 0);
+                  next0 = UDP_INPUT_NEXT_ICMP4_ERROR;
+                }
+              else
+                {
+                  icmp6_error_set_vnet_buffer(b0, ICMP6_destination_unreachable,
+                        ICMP6_destination_unreachable_port_unreachable, 0);
+                  next0 = UDP_INPUT_NEXT_ICMP6_ERROR;
+                }
+                n_no_listener ++;
+            }
+          else
+            {
+              b0->error = node->errors[UDP_ERROR_NONE];
+              // advance to the payload
+              vlib_buffer_advance (b0, sizeof (*h0));
+            }
+
+          if (PREDICT_FALSE(next1 == SPARSE_VEC_INVALID_INDEX))
+            {
+              // move the pointer back so icmp-error can find the
+              // ip packet header
+              vlib_buffer_advance (b1, - (word)advance1);
+
+              if (is_ip4)
+                {
+                  icmp4_error_set_vnet_buffer(b1, ICMP4_destination_unreachable,
+                        ICMP4_destination_unreachable_port_unreachable, 0);
+                  next1 = UDP_INPUT_NEXT_ICMP4_ERROR;
+                }
+              else
+                {
+                  icmp6_error_set_vnet_buffer(b1, ICMP6_destination_unreachable,
+                        ICMP6_destination_unreachable_port_unreachable, 0);
+                  next1 = UDP_INPUT_NEXT_ICMP6_ERROR;
+                }
+                n_no_listener ++;
+            }
+          else
+            {
+              b1->error = node->errors[UDP_ERROR_NONE];
+              // advance to the payload
+              vlib_buffer_advance (b1, sizeof (*h1));
+            }
           
           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
             {
@@ -190,6 +244,8 @@ udp46_input_inline (vlib_main_t * vm,
                 {
                   tr->src_port = h0->src_port;
                   tr->dst_port = h0->dst_port;
+                  tr->bound = (next0 != UDP_INPUT_NEXT_ICMP4_ERROR &&
+                               next0 != UDP_INPUT_NEXT_ICMP6_ERROR);
                 }
             }
           if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) 
@@ -200,12 +256,11 @@ udp46_input_inline (vlib_main_t * vm,
                 {
                   tr->src_port = h1->src_port;
                   tr->dst_port = h1->dst_port;
+                  tr->bound = (next1 != UDP_INPUT_NEXT_ICMP4_ERROR &&
+                               next1 != UDP_INPUT_NEXT_ICMP6_ERROR);
                 }
             }
 
-          vlib_buffer_advance (b0, sizeof (*h0));
-          vlib_buffer_advance (b1, sizeof (*h1));
-
          vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
                                           to_next, n_left_to_next,
                                           bi0, bi1, next0, next1);
@@ -251,7 +306,32 @@ udp46_input_inline (vlib_main_t * vm,
               i0 = sparse_vec_index (rt->next_by_dst_port, h0->dst_port);
               next0 = vec_elt(rt->next_by_dst_port, i0);
 
-              b0->error = node->errors [next0 == SPARSE_VEC_INVALID_INDEX ? UDP_ERROR_NO_LISTENER : UDP_ERROR_NONE];
+              if (PREDICT_FALSE(next0 == SPARSE_VEC_INVALID_INDEX))
+                {
+                  // move the pointer back so icmp-error can find the
+                  // ip packet header
+                  vlib_buffer_advance (b0, - (word)advance0);
+
+                  if (is_ip4)
+                    {
+                      icmp4_error_set_vnet_buffer(b0, ICMP4_destination_unreachable,
+                            ICMP4_destination_unreachable_port_unreachable, 0);
+                      next0 = UDP_INPUT_NEXT_ICMP4_ERROR;
+                    }
+                  else
+                    {
+                      icmp6_error_set_vnet_buffer(b0, ICMP6_destination_unreachable,
+                            ICMP6_destination_unreachable_port_unreachable, 0);
+                      next0 = UDP_INPUT_NEXT_ICMP6_ERROR;
+                    }
+                    n_no_listener ++;
+                }
+              else
+                {
+                  b0->error = node->errors[UDP_ERROR_NONE];
+                  // advance to the payload
+                  vlib_buffer_advance (b0, sizeof (*h0));
+                }
             }
           else
             {
@@ -268,9 +348,11 @@ udp46_input_inline (vlib_main_t * vm,
                 {
                   tr->src_port = h0->src_port;
                   tr->dst_port = h0->dst_port;
+                  tr->bound = (next0 != UDP_INPUT_NEXT_ICMP4_ERROR &&
+                               next0 != UDP_INPUT_NEXT_ICMP6_ERROR);
                 }
             }
-          vlib_buffer_advance (b0, sizeof (*h0));
+
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
                                           to_next, n_left_to_next,
                                           bi0, next0);
@@ -278,6 +360,7 @@ udp46_input_inline (vlib_main_t * vm,
 
       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     }
+  vlib_error_count(vm, node->node_index, UDP_ERROR_NO_LISTENER, n_no_listener);
   return from_frame->n_vectors;
 }