tcp: move accept init to separate reusable function
[vpp.git] / src / vnet / tcp / tcp_input.c
index 78f6215..0757741 100755 (executable)
@@ -266,6 +266,98 @@ tcp_update_timestamp (tcp_connection_t * tc, u32 seq, u32 seq_end)
     }
 }
 
+static void
+tcp_handle_rst (tcp_connection_t * tc)
+{
+  switch (tc->rst_state)
+    {
+    case TCP_STATE_SYN_RCVD:
+      /* Cleanup everything. App wasn't notified yet */
+      session_transport_delete_notify (&tc->connection);
+      tcp_connection_cleanup (tc);
+      break;
+    case TCP_STATE_SYN_SENT:
+      session_stream_connect_notify (&tc->connection, 1 /* fail */ );
+      tcp_connection_cleanup (tc);
+      break;
+    case TCP_STATE_ESTABLISHED:
+      session_transport_reset_notify (&tc->connection);
+      session_transport_closed_notify (&tc->connection);
+      break;
+    case TCP_STATE_CLOSE_WAIT:
+    case TCP_STATE_FIN_WAIT_1:
+    case TCP_STATE_FIN_WAIT_2:
+    case TCP_STATE_CLOSING:
+    case TCP_STATE_LAST_ACK:
+      session_transport_closed_notify (&tc->connection);
+      break;
+    case TCP_STATE_CLOSED:
+    case TCP_STATE_TIME_WAIT:
+      break;
+    default:
+      TCP_DBG ("reset state: %u", tc->state);
+    }
+}
+
+static void
+tcp_program_reset_ntf (tcp_worker_ctx_t * wrk, tcp_connection_t * tc)
+{
+  if (!tcp_disconnect_pending (tc))
+    {
+      tc->rst_state = tc->state;
+      vec_add1 (wrk->pending_resets, tc->c_c_index);
+      tcp_disconnect_pending_on (tc);
+    }
+}
+
+/**
+ * Handle reset packet
+ *
+ * Programs disconnect/reset notification that should be sent
+ * later by calling @ref tcp_handle_disconnects
+ */
+static void
+tcp_rcv_rst (tcp_worker_ctx_t * wrk, tcp_connection_t * tc)
+{
+  TCP_EVT (TCP_EVT_RST_RCVD, tc);
+  switch (tc->state)
+    {
+    case TCP_STATE_SYN_RCVD:
+      tcp_program_reset_ntf (wrk, tc);
+      tcp_connection_set_state (tc, TCP_STATE_CLOSED);
+      break;
+    case TCP_STATE_SYN_SENT:
+      /* Do not program ntf because the connection is half-open */
+      tcp_handle_rst (tc);
+      break;
+    case TCP_STATE_ESTABLISHED:
+      tcp_connection_timers_reset (tc);
+      tcp_cong_recovery_off (tc);
+      tcp_program_reset_ntf (wrk, tc);
+      tcp_connection_set_state (tc, TCP_STATE_CLOSED);
+      tcp_program_cleanup (wrk, tc);
+      break;
+    case TCP_STATE_CLOSE_WAIT:
+    case TCP_STATE_FIN_WAIT_1:
+    case TCP_STATE_FIN_WAIT_2:
+    case TCP_STATE_CLOSING:
+    case TCP_STATE_LAST_ACK:
+      tcp_connection_timers_reset (tc);
+      tcp_cong_recovery_off (tc);
+      tcp_program_reset_ntf (wrk, tc);
+      /* Make sure we mark the session as closed. In some states we may
+       * be still trying to send data */
+      tcp_connection_set_state (tc, TCP_STATE_CLOSED);
+      tcp_program_cleanup (wrk, tc);
+      break;
+    case TCP_STATE_CLOSED:
+    case TCP_STATE_TIME_WAIT:
+      break;
+    default:
+      TCP_DBG ("reset state: %u", tc->state);
+    }
+}
+
 /**
  * Validate incoming segment as per RFC793 p. 69 and RFC1323 p. 19
  *
@@ -392,7 +484,7 @@ tcp_segment_validate (tcp_worker_ctx_t * wrk, tcp_connection_t * tc0,
   /* 2nd: check the RST bit */
   if (PREDICT_FALSE (tcp_rst (th0)))
     {
-      tcp_connection_reset (tc0);
+      tcp_rcv_rst (wrk, tc0);
       *error0 = TCP_ERROR_RST_RCVD;
       goto error;
     }
@@ -608,6 +700,9 @@ tcp_handle_postponed_dequeues (tcp_worker_ctx_t * wrk)
            tc->flags &= ~TCP_CONN_PSH_PENDING;
        }
 
+      if (tcp_is_descheduled (tc))
+       tcp_reschedule (tc);
+
       /* If everything has been acked, stop retransmit timer
        * otherwise update. */
       tcp_retransmit_timer_update (tc);
@@ -1002,9 +1097,7 @@ tcp_rcv_sacks (tcp_connection_t * tc, u32 ack)
     {
       if (seq_lt (blk->start, blk->end)
          && seq_gt (blk->start, tc->snd_una)
-         && seq_gt (blk->start, ack)
-         && seq_lt (blk->start, tc->snd_nxt)
-         && seq_leq (blk->end, tc->snd_nxt))
+         && seq_gt (blk->start, ack) && seq_leq (blk->end, tc->snd_nxt))
        {
          blk++;
          continue;
@@ -1222,7 +1315,12 @@ tcp_update_snd_wnd (tcp_connection_t * tc, u32 seq, u32 ack, u32 snd_wnd)
        }
       else
        {
-         tcp_persist_timer_reset (tc);
+         if (PREDICT_FALSE (tcp_timer_is_active (tc, TCP_TIMER_PERSIST)))
+           tcp_persist_timer_reset (tc);
+
+         if (PREDICT_FALSE (tcp_is_descheduled (tc)))
+           tcp_reschedule (tc);
+
          if (PREDICT_FALSE (!tcp_in_recovery (tc) && tc->rto_boff > 0))
            {
              tc->rto_boff = 0;
@@ -1373,6 +1471,7 @@ tcp_cc_recover (tcp_connection_t * tc)
   ASSERT (tc->rto_boff == 0);
   ASSERT (!tcp_in_cong_recovery (tc));
   ASSERT (tcp_scoreboard_is_sane_post_recovery (tc));
+
   return is_spurious;
 }
 
@@ -1676,22 +1775,35 @@ tcp_program_disconnect (tcp_worker_ctx_t * wrk, tcp_connection_t * tc)
 static void
 tcp_handle_disconnects (tcp_worker_ctx_t * wrk)
 {
-  u32 thread_index, *pending_disconnects;
+  u32 thread_index, *pending_disconnects, *pending_resets;
   tcp_connection_t *tc;
   int i;
 
-  if (!vec_len (wrk->pending_disconnects))
-    return;
+  if (vec_len (wrk->pending_disconnects))
+    {
+      thread_index = wrk->vm->thread_index;
+      pending_disconnects = wrk->pending_disconnects;
+      for (i = 0; i < vec_len (pending_disconnects); i++)
+       {
+         tc = tcp_connection_get (pending_disconnects[i], thread_index);
+         tcp_disconnect_pending_off (tc);
+         session_transport_closing_notify (&tc->connection);
+       }
+      _vec_len (wrk->pending_disconnects) = 0;
+    }
 
-  thread_index = wrk->vm->thread_index;
-  pending_disconnects = wrk->pending_disconnects;
-  for (i = 0; i < vec_len (pending_disconnects); i++)
+  if (vec_len (wrk->pending_resets))
     {
-      tc = tcp_connection_get (pending_disconnects[i], thread_index);
-      tcp_disconnect_pending_off (tc);
-      session_transport_closing_notify (&tc->connection);
+      thread_index = wrk->vm->thread_index;
+      pending_resets = wrk->pending_resets;
+      for (i = 0; i < vec_len (pending_resets); i++)
+       {
+         tc = tcp_connection_get (pending_resets[i], thread_index);
+         tcp_disconnect_pending_off (tc);
+         tcp_handle_rst (tc);
+       }
+      _vec_len (wrk->pending_resets) = 0;
     }
-  _vec_len (wrk->pending_disconnects) = 0;
 }
 
 static void
@@ -1972,7 +2084,8 @@ tcp_segment_rcv (tcp_worker_ctx_t * wrk, tcp_connection_t * tc,
           * retransmissions since we may not have any data to send */
          if (seq_leq (vnet_buffer (b)->tcp.seq_end, tc->rcv_nxt))
            {
-             tcp_program_ack (tc);
+             tcp_program_dupack (tc);
+             tc->errors.below_data_wnd++;
              error = TCP_ERROR_SEGMENT_OLD;
              goto done;
            }
@@ -2556,7 +2669,7 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          /* If ACK is acceptable, signal client that peer is not
           * willing to accept connection and drop connection*/
          if (tcp_ack (tcp0))
-           tcp_connection_reset (tc0);
+           tcp_rcv_rst (wrk, tc0);
          error0 = TCP_ERROR_RST_RCVD;
          goto drop;
        }
@@ -2701,6 +2814,7 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
                                              my_thread_index);
   tcp_inc_counter (syn_sent, TCP_ERROR_MSG_QUEUE_FULL, errors);
   vlib_buffer_free (vm, first_buffer, from_frame->n_vectors);
+  tcp_handle_disconnects (wrk);
 
   return from_frame->n_vectors;
 }
@@ -2837,7 +2951,7 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          /* Make sure the segment is exactly right */
          if (tc0->rcv_nxt != vnet_buffer (b0)->tcp.seq_number || is_fin0)
            {
-             tcp_connection_reset (tc0);
+             tcp_send_reset_w_pkt (tc0, b0, thread_index, is_ip4);
              error0 = TCP_ERROR_SEGMENT_INVALID;
              goto drop;
            }
@@ -2850,7 +2964,8 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
           */
          if (tcp_rcv_ack_no_cc (tc0, b0, &error0))
            {
-             tcp_connection_reset (tc0);
+             tcp_send_reset_w_pkt (tc0, b0, thread_index, is_ip4);
+             error0 = TCP_ERROR_SEGMENT_INVALID;
              goto drop;
            }
 
@@ -2877,7 +2992,9 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          if (session_stream_accept_notify (&tc0->connection))
            {
              error0 = TCP_ERROR_MSG_QUEUE_FULL;
-             tcp_connection_reset (tc0);
+             tcp_send_reset (tc0);
+             session_transport_delete_notify (&tc0->connection);
+             tcp_connection_cleanup (tc0);
              goto drop;
            }
          error0 = TCP_ERROR_ACK_OK;
@@ -2920,9 +3037,8 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
              if (tc0->flags & TCP_CONN_FINRCVD)
                {
                  tcp_connection_set_state (tc0, TCP_STATE_CLOSED);
-                 tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE,
-                                tcp_cfg.cleanup_time);
                  session_transport_closed_notify (&tc0->connection);
+                 tcp_program_cleanup (wrk, tc0);
                  goto drop;
                }
 
@@ -3003,7 +3119,7 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
           * we can't ensure that we have no packets already enqueued
           * to output. Rely instead on the waitclose timer */
          tcp_connection_timers_reset (tc0);
-         tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, tcp_cfg.cleanup_time);
+         tcp_program_cleanup (tcp_get_worker (tc0->c_thread_index), tc0);
 
          goto drop;
 
@@ -3198,54 +3314,46 @@ tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
                     vlib_frame_t * from_frame, int is_ip4)
 {
   u32 n_left_from, *from, n_syns = 0, *first_buffer;
-  u32 my_thread_index = vm->thread_index;
-  tcp_connection_t *tc0;
+  u32 thread_index = vm->thread_index;
 
   from = first_buffer = vlib_frame_vector_args (from_frame);
   n_left_from = from_frame->n_vectors;
 
   while (n_left_from > 0)
     {
-      u32 bi0;
-      vlib_buffer_t *b0;
-      tcp_rx_trace_t *t0;
-      tcp_header_t *th0 = 0;
-      tcp_connection_t *lc0;
-      ip4_header_t *ip40;
-      ip6_header_t *ip60;
-      tcp_connection_t *child0;
-      u32 error0 = TCP_ERROR_NONE;
+      u32 bi, error = TCP_ERROR_NONE;
+      tcp_connection_t *lc, *child;
+      vlib_buffer_t *b;
 
-      bi0 = from[0];
+      bi = from[0];
       from += 1;
       n_left_from -= 1;
 
-      b0 = vlib_get_buffer (vm, bi0);
+      b = vlib_get_buffer (vm, bi);
 
-      if (is_ip4)
+      lc = tcp_listener_get (vnet_buffer (b)->tcp.connection_index);
+      if (PREDICT_FALSE (lc == 0))
        {
-         ip40 = vlib_buffer_get_current (b0);
-         th0 = tcp_buffer_hdr (b0);
-       }
-      else
-       {
-         ip60 = vlib_buffer_get_current (b0);
-         th0 = tcp_buffer_hdr (b0);
-       }
-
-      lc0 = tcp_listener_get (vnet_buffer (b0)->tcp.connection_index);
-      if (PREDICT_FALSE (lc0 == 0))
-       {
-         tc0 = tcp_connection_get (vnet_buffer (b0)->tcp.connection_index,
-                                   my_thread_index);
-         if (tc0->state != TCP_STATE_TIME_WAIT)
+         tcp_connection_t *tc;
+         tc = tcp_connection_get (vnet_buffer (b)->tcp.connection_index,
+                                  thread_index);
+         if (tc->state != TCP_STATE_TIME_WAIT)
            {
-             error0 = TCP_ERROR_CREATE_EXISTS;
-             goto drop;
+             error = TCP_ERROR_CREATE_EXISTS;
+             goto done;
            }
-         lc0 = tcp_lookup_listener (b0, tc0->c_fib_index, is_ip4);
+         lc = tcp_lookup_listener (b, tc->c_fib_index, is_ip4);
          /* clean up the old session */
-         tcp_connection_del (tc0);
+         tcp_connection_del (tc);
+       }
+
+      /* Make sure connection wasn't just created */
+      child = tcp_lookup_connection (lc->c_fib_index, b, thread_index,
+                                    is_ip4);
+      if (PREDICT_FALSE (child->state != TCP_STATE_LISTEN))
+       {
+         error = TCP_ERROR_CREATE_EXISTS;
+         goto done;
        }
 
       /* Create child session. For syn-flood protection use filter */
@@ -3265,91 +3373,51 @@ tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 
       /* 3. check for a SYN (did that already) */
 
-      /* Make sure connection wasn't just created */
-      child0 = tcp_lookup_connection (lc0->c_fib_index, b0, my_thread_index,
-                                     is_ip4);
-      if (PREDICT_FALSE (child0->state != TCP_STATE_LISTEN))
-       {
-         error0 = TCP_ERROR_CREATE_EXISTS;
-         goto drop;
-       }
-
       /* Create child session and send SYN-ACK */
-      child0 = tcp_connection_alloc (my_thread_index);
-      child0->c_lcl_port = th0->dst_port;
-      child0->c_rmt_port = th0->src_port;
-      child0->c_is_ip4 = is_ip4;
-      child0->state = TCP_STATE_SYN_RCVD;
-      child0->c_fib_index = lc0->c_fib_index;
-      child0->cc_algo = lc0->cc_algo;
+      child = tcp_connection_alloc (thread_index);
 
-      if (is_ip4)
+      if (tcp_options_parse (tcp_buffer_hdr (b), &child->rcv_opts, 1))
        {
-         child0->c_lcl_ip4.as_u32 = ip40->dst_address.as_u32;
-         child0->c_rmt_ip4.as_u32 = ip40->src_address.as_u32;
-       }
-      else
-       {
-         clib_memcpy_fast (&child0->c_lcl_ip6, &ip60->dst_address,
-                           sizeof (ip6_address_t));
-         clib_memcpy_fast (&child0->c_rmt_ip6, &ip60->src_address,
-                           sizeof (ip6_address_t));
+         error = TCP_ERROR_OPTIONS;
+         tcp_connection_free (child);
+         goto done;
        }
 
-      if (tcp_options_parse (th0, &child0->rcv_opts, 1))
-       {
-         error0 = TCP_ERROR_OPTIONS;
-         tcp_connection_free (child0);
-         goto drop;
-       }
+      tcp_init_w_buffer (child, b, is_ip4);
 
-      child0->irs = vnet_buffer (b0)->tcp.seq_number;
-      child0->rcv_nxt = vnet_buffer (b0)->tcp.seq_number + 1;
-      child0->rcv_las = child0->rcv_nxt;
-      child0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+      child->state = TCP_STATE_SYN_RCVD;
+      child->c_fib_index = lc->c_fib_index;
+      child->cc_algo = lc->cc_algo;
+      tcp_connection_init_vars (child);
+      child->rto = TCP_RTO_MIN;
 
-      /* RFC1323: TSval timestamps sent on {SYN} and {SYN,ACK}
-       * segments are used to initialize PAWS. */
-      if (tcp_opts_tstamp (&child0->rcv_opts))
+      if (session_stream_accept (&child->connection, lc->c_s_index,
+                                lc->c_thread_index, 0 /* notify */ ))
        {
-         child0->tsval_recent = child0->rcv_opts.tsval;
-         child0->tsval_recent_age = tcp_time_now ();
+         tcp_connection_cleanup (child);
+         error = TCP_ERROR_CREATE_SESSION_FAIL;
+         goto done;
        }
 
-      if (tcp_opts_wscale (&child0->rcv_opts))
-       child0->snd_wscale = child0->rcv_opts.wscale;
-
-      child0->snd_wnd = clib_net_to_host_u16 (th0->window)
-       << child0->snd_wscale;
-      child0->snd_wl1 = vnet_buffer (b0)->tcp.seq_number;
-      child0->snd_wl2 = vnet_buffer (b0)->tcp.ack_number;
+      child->tx_fifo_size = transport_tx_fifo_size (&child->connection);
 
-      tcp_connection_init_vars (child0);
-      child0->rto = TCP_RTO_MIN;
-
-      if (session_stream_accept (&child0->connection, lc0->c_s_index,
-                                lc0->c_thread_index, 0 /* notify */ ))
-       {
-         tcp_connection_cleanup (child0);
-         error0 = TCP_ERROR_CREATE_SESSION_FAIL;
-         goto drop;
-       }
+      tcp_send_synack (child);
 
-      TCP_EVT (TCP_EVT_SYN_RCVD, child0, 1);
-      child0->tx_fifo_size = transport_tx_fifo_size (&child0->connection);
-      tcp_send_synack (child0);
+      TCP_EVT (TCP_EVT_SYN_RCVD, child, 1);
 
-    drop:
+    done:
 
-      if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+      if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
        {
-         t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
-         clib_memcpy_fast (&t0->tcp_header, th0, sizeof (t0->tcp_header));
-         clib_memcpy_fast (&t0->tcp_connection, lc0,
-                           sizeof (t0->tcp_connection));
+         tcp_rx_trace_t *t;
+         t = vlib_add_trace (vm, node, b, sizeof (*t));
+         clib_memcpy_fast (&t->tcp_header, tcp_buffer_hdr (b),
+                           sizeof (t->tcp_header));
+         clib_memcpy_fast (&t->tcp_connection, lc,
+                           sizeof (t->tcp_connection));
        }
 
-      n_syns += (error0 == TCP_ERROR_NONE);
+      n_syns += (error == TCP_ERROR_NONE);
     }
 
   tcp_inc_counter (listen, TCP_ERROR_SYNS_RCVD, n_syns);
@@ -3579,27 +3647,25 @@ tcp_input_lookup_buffer (vlib_buffer_t * b, u8 thread_index, u32 * error,
 
 static inline void
 tcp_input_dispatch_buffer (tcp_main_t * tm, tcp_connection_t * tc,
-                          vlib_buffer_t * b, u16 * next, u32 * error)
+                          vlib_buffer_t * b, u16 * next,
+                          vlib_node_runtime_t * error_node)
 {
   tcp_header_t *tcp;
+  u32 error;
   u8 flags;
 
   tcp = tcp_buffer_hdr (b);
   flags = tcp->flags & filter_flags;
   *next = tm->dispatch_table[tc->state][flags].next;
-  *error = tm->dispatch_table[tc->state][flags].error;
+  error = tm->dispatch_table[tc->state][flags].error;
   tc->segs_in += 1;
 
-  if (PREDICT_FALSE (*error == TCP_ERROR_DISPATCH
-                    || *next == TCP_INPUT_NEXT_RESET))
+  if (PREDICT_FALSE (error != TCP_ERROR_NONE))
     {
-      /* Overload tcp flags to store state */
-      tcp_state_t state = tc->state;
-      vnet_buffer (b)->tcp.flags = tc->state;
-
-      if (*error == TCP_ERROR_DISPATCH)
+      b->error = error_node->errors[error];
+      if (error == TCP_ERROR_DISPATCH)
        clib_warning ("tcp conn %u disp error state %U flags %U",
-                     tc->c_c_index, format_tcp_state, state,
+                     tc->c_c_index, format_tcp_state, tc->state,
                      format_tcp_flags, (int) flags);
     }
 }
@@ -3612,9 +3678,11 @@ tcp46_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
   tcp_main_t *tm = vnet_get_tcp_main ();
   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
   u16 nexts[VLIB_FRAME_SIZE], *next;
+  vlib_node_runtime_t *error_node;
 
   tcp_set_time_now (tcp_get_worker (thread_index));
 
+  error_node = vlib_node_get_runtime (vm, tcp_node_index (input, is_ip4));
   from = vlib_frame_vector_args (frame);
   n_left_from = frame->n_vectors;
   vlib_get_buffers (vm, from, bufs, n_left_from);
@@ -3650,8 +3718,8 @@ tcp46_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          vnet_buffer (b[0])->tcp.connection_index = tc0->c_c_index;
          vnet_buffer (b[1])->tcp.connection_index = tc1->c_c_index;
 
-         tcp_input_dispatch_buffer (tm, tc0, b[0], &next[0], &error0);
-         tcp_input_dispatch_buffer (tm, tc1, b[1], &next[1], &error1);
+         tcp_input_dispatch_buffer (tm, tc0, b[0], &next[0], error_node);
+         tcp_input_dispatch_buffer (tm, tc1, b[1], &next[1], error_node);
        }
       else
        {
@@ -3659,19 +3727,25 @@ tcp46_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
            {
              ASSERT (tcp_lookup_is_valid (tc0, b[0], tcp_buffer_hdr (b[0])));
              vnet_buffer (b[0])->tcp.connection_index = tc0->c_c_index;
-             tcp_input_dispatch_buffer (tm, tc0, b[0], &next[0], &error0);
+             tcp_input_dispatch_buffer (tm, tc0, b[0], &next[0], error_node);
            }
          else
-           tcp_input_set_error_next (tm, &next[0], &error0, is_ip4);
+           {
+             tcp_input_set_error_next (tm, &next[0], &error0, is_ip4);
+             b[0]->error = error_node->errors[error0];
+           }
 
          if (PREDICT_TRUE (tc1 != 0))
            {
              ASSERT (tcp_lookup_is_valid (tc1, b[1], tcp_buffer_hdr (b[1])));
              vnet_buffer (b[1])->tcp.connection_index = tc1->c_c_index;
-             tcp_input_dispatch_buffer (tm, tc1, b[1], &next[1], &error1);
+             tcp_input_dispatch_buffer (tm, tc1, b[1], &next[1], error_node);
            }
          else
-           tcp_input_set_error_next (tm, &next[1], &error1, is_ip4);
+           {
+             tcp_input_set_error_next (tm, &next[1], &error1, is_ip4);
+             b[1]->error = error_node->errors[error1];
+           }
        }
 
       b += 2;
@@ -3696,10 +3770,13 @@ tcp46_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
        {
          ASSERT (tcp_lookup_is_valid (tc0, b[0], tcp_buffer_hdr (b[0])));
          vnet_buffer (b[0])->tcp.connection_index = tc0->c_c_index;
-         tcp_input_dispatch_buffer (tm, tc0, b[0], &next[0], &error0);
+         tcp_input_dispatch_buffer (tm, tc0, b[0], &next[0], error_node);
        }
       else
-       tcp_input_set_error_next (tm, &next[0], &error0, is_ip4);
+       {
+         tcp_input_set_error_next (tm, &next[0], &error0, is_ip4);
+         b[0]->error = error_node->errors[error0];
+       }
 
       b += 1;
       next += 1;
@@ -3860,7 +3937,7 @@ do {                                                              \
   _(LISTEN, TCP_FLAG_FIN | TCP_FLAG_RST, TCP_INPUT_NEXT_DROP,
     TCP_ERROR_SEGMENT_INVALID);
   _(LISTEN, TCP_FLAG_FIN | TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_DROP,
-    TCP_ERROR_NONE);
+    TCP_ERROR_SEGMENT_INVALID);
   _(LISTEN, TCP_FLAG_FIN | TCP_FLAG_SYN, TCP_INPUT_NEXT_DROP,
     TCP_ERROR_SEGMENT_INVALID);
   _(LISTEN, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_ACK, TCP_INPUT_NEXT_DROP,
@@ -4047,10 +4124,10 @@ do {                                                            \
   _(CLOSED, TCP_FLAG_RST, TCP_INPUT_NEXT_DROP, TCP_ERROR_CONNECTION_CLOSED);
   _(CLOSED, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_DROP,
     TCP_ERROR_CONNECTION_CLOSED);
-  _(CLOSED, TCP_FLAG_ACK, TCP_INPUT_NEXT_RESET, TCP_ERROR_NONE);
-  _(CLOSED, TCP_FLAG_SYN, TCP_INPUT_NEXT_RESET, TCP_ERROR_NONE);
+  _(CLOSED, TCP_FLAG_ACK, TCP_INPUT_NEXT_RESET, TCP_ERROR_CONNECTION_CLOSED);
+  _(CLOSED, TCP_FLAG_SYN, TCP_INPUT_NEXT_RESET, TCP_ERROR_CONNECTION_CLOSED);
   _(CLOSED, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RESET,
-    TCP_ERROR_NONE);
+    TCP_ERROR_CONNECTION_CLOSED);
 #undef _
 }