tcp: add more last-ack arcs
[vpp.git] / src / vnet / tcp / tcp_input.c
index e15ad73..ee7f4ae 100644 (file)
@@ -121,10 +121,11 @@ tcp_segment_in_rcv_wnd (tcp_connection_t * tc, u32 seq, u32 end_seq)
  *
  * @param th TCP header
  * @param to TCP options data structure to be populated
+ * @param is_syn set if packet is syn
  * @return -1 if parsing failed
  */
-static int
-tcp_options_parse (tcp_header_t * th, tcp_options_t * to)
+static inline int
+tcp_options_parse (tcp_header_t * th, tcp_options_t * to, u8 is_syn)
 {
   const u8 *data;
   u8 opt_len, opts_len, kind;
@@ -136,7 +137,7 @@ tcp_options_parse (tcp_header_t * th, tcp_options_t * to)
 
   /* Zero out all flags but those set in SYN */
   to->flags &= (TCP_OPTS_FLAG_SACK_PERMITTED | TCP_OPTS_FLAG_WSCALE
-               | TCP_OPTS_FLAG_SACK);
+               | TCP_OPTS_FLAG_TSTAMP | TCP_OPTION_MSS);
 
   for (; opts_len > 0; opts_len -= opt_len, data += opt_len)
     {
@@ -166,6 +167,8 @@ tcp_options_parse (tcp_header_t * th, tcp_options_t * to)
       switch (kind)
        {
        case TCP_OPTION_MSS:
+         if (!is_syn)
+           break;
          if ((opt_len == TCP_OPTION_LEN_MSS) && tcp_syn (th))
            {
              to->flags |= TCP_OPTS_FLAG_MSS;
@@ -173,27 +176,29 @@ tcp_options_parse (tcp_header_t * th, tcp_options_t * to)
            }
          break;
        case TCP_OPTION_WINDOW_SCALE:
+         if (!is_syn)
+           break;
          if ((opt_len == TCP_OPTION_LEN_WINDOW_SCALE) && tcp_syn (th))
            {
              to->flags |= TCP_OPTS_FLAG_WSCALE;
              to->wscale = data[2];
              if (to->wscale > TCP_MAX_WND_SCALE)
-               {
-                 clib_warning ("Illegal window scaling value: %d",
-                               to->wscale);
-                 to->wscale = TCP_MAX_WND_SCALE;
-               }
+               to->wscale = TCP_MAX_WND_SCALE;
            }
          break;
        case TCP_OPTION_TIMESTAMP:
-         if (opt_len == TCP_OPTION_LEN_TIMESTAMP)
+         if (is_syn)
+           to->flags |= TCP_OPTS_FLAG_TSTAMP;
+         if ((to->flags & TCP_OPTS_FLAG_TSTAMP)
+             && opt_len == TCP_OPTION_LEN_TIMESTAMP)
            {
-             to->flags |= TCP_OPTS_FLAG_TSTAMP;
              to->tsval = clib_net_to_host_u32 (*(u32 *) (data + 2));
              to->tsecr = clib_net_to_host_u32 (*(u32 *) (data + 6));
            }
          break;
        case TCP_OPTION_SACK_PERMITTED:
+         if (!is_syn)
+           break;
          if (opt_len == TCP_OPTION_LEN_SACK_PERMITTED && tcp_syn (th))
            to->flags |= TCP_OPTS_FLAG_SACK_PERMITTED;
          break;
@@ -236,7 +241,7 @@ tcp_options_parse (tcp_header_t * th, tcp_options_t * to)
 always_inline int
 tcp_segment_check_paws (tcp_connection_t * tc)
 {
-  return tcp_opts_tstamp (&tc->rcv_opts) && tc->tsval_recent
+  return tcp_opts_tstamp (&tc->rcv_opts)
     && timestamp_lt (tc->rcv_opts.tsval, tc->tsval_recent);
 }
 
@@ -289,9 +294,8 @@ tcp_segment_validate (tcp_worker_ctx_t * wrk, tcp_connection_t * tc0,
       goto error;
     }
 
-  if (PREDICT_FALSE (tcp_options_parse (th0, &tc0->rcv_opts)))
+  if (PREDICT_FALSE (tcp_options_parse (th0, &tc0->rcv_opts, 0)))
     {
-      clib_warning ("options parse error");
       *error0 = TCP_ERROR_OPTIONS;
       goto error;
     }
@@ -299,8 +303,6 @@ tcp_segment_validate (tcp_worker_ctx_t * wrk, tcp_connection_t * tc0,
   if (PREDICT_FALSE (tcp_segment_check_paws (tc0)))
     {
       *error0 = TCP_ERROR_PAWS;
-      if (CLIB_DEBUG > 2)
-       clib_warning ("paws failed\n%U", format_tcp_connection, tc0, 2);
       TCP_EVT_DBG (TCP_EVT_PAWS_FAIL, tc0, vnet_buffer (b0)->tcp.seq_number,
                   vnet_buffer (b0)->tcp.seq_end);
 
@@ -309,20 +311,19 @@ tcp_segment_validate (tcp_worker_ctx_t * wrk, tcp_connection_t * tc0,
       if (timestamp_lt (tc0->tsval_recent_age + TCP_PAWS_IDLE,
                        tcp_time_now_w_thread (tc0->c_thread_index)))
        {
-         /* Age isn't reset until we get a valid tsval (bsd inspired) */
-         tc0->tsval_recent = 0;
-         clib_warning ("paws failed - really old segment. REALLY?");
+         tc0->tsval_recent = tc0->rcv_opts.tsval;
+         clib_warning ("paws failed: 24-day old segment");
        }
-      else
+      /* Drop after ack if not rst. Resets can fail paws check as per
+       * RFC 7323 sec. 5.2: When an <RST> segment is received, it MUST NOT
+       * be subjected to the PAWS check by verifying an acceptable value in
+       * SEG.TSval */
+      else if (!tcp_rst (th0))
        {
-         /* Drop after ack if not rst */
-         if (!tcp_rst (th0))
-           {
-             tcp_program_ack (wrk, tc0);
-             TCP_EVT_DBG (TCP_EVT_DUPACK_SENT, tc0, vnet_buffer (b0)->tcp);
-           }
+         tcp_program_ack (wrk, tc0);
+         TCP_EVT_DBG (TCP_EVT_DUPACK_SENT, tc0, vnet_buffer (b0)->tcp);
+         goto error;
        }
-      goto error;
     }
 
   /* 1st: check sequence number */
@@ -361,6 +362,7 @@ tcp_segment_validate (tcp_worker_ctx_t * wrk, tcp_connection_t * tc0,
       /* TODO implement RFC 5961 */
       if (tc0->state == TCP_STATE_SYN_RCVD)
        {
+         tcp_options_parse (th0, &tc0->rcv_opts, 1);
          tcp_send_synack (tc0);
          TCP_EVT_DBG (TCP_EVT_SYN_RCVD, tc0, 0);
        }
@@ -507,6 +509,7 @@ tcp_estimate_initial_rtt (tcp_connection_t * tc)
 
   if (mrtt > 0 && mrtt < TCP_RTT_MAX)
     tcp_estimate_rtt (tc, mrtt);
+  tcp_update_rto (tc);
 }
 
 /**
@@ -537,6 +540,12 @@ tcp_handle_postponed_dequeues (tcp_worker_ctx_t * wrk)
       tc->burst_acked = 0;
       tcp_validate_txf_size (tc, tc->snd_una_max - tc->snd_una);
 
+      if (PREDICT_FALSE (tc->flags & TCP_CONN_PSH_PENDING))
+       {
+         if (seq_leq (tc->psh_seq, tc->snd_una))
+           tc->flags &= ~TCP_CONN_PSH_PENDING;
+       }
+
       /* If everything has been acked, stop retransmit timer
        * otherwise update. */
       tcp_retransmit_timer_update (tc);
@@ -899,7 +908,9 @@ 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_leq (blk->end, tc->snd_una_max))
+         && seq_gt (blk->start, ack)
+         && seq_lt (blk->start, tc->snd_una_max)
+         && seq_leq (blk->end, tc->snd_una_max))
        {
          blk++;
          continue;
@@ -1584,7 +1595,10 @@ process_ack:
     {
       tcp_cc_handle_event (tc, is_dack);
       if (!tcp_in_cong_recovery (tc))
-       return 0;
+       {
+         *error = TCP_ERROR_ACK_OK;
+         return 0;
+       }
       *error = TCP_ERROR_ACK_DUP;
       if (vnet_buffer (b)->tcp.data_len || tcp_is_fin (th))
        return 0;
@@ -1634,13 +1648,12 @@ static void
 tcp_rcv_fin (tcp_worker_ctx_t * wrk, tcp_connection_t * tc, vlib_buffer_t * b,
             u32 * error)
 {
+  /* Account for the FIN and send ack */
+  tc->rcv_nxt += 1;
+  tcp_program_ack (wrk, tc);
   /* Enter CLOSE-WAIT and notify session. To avoid lingering
    * in CLOSE-WAIT, set timer (reuse WAITCLOSE). */
-  /* Account for the FIN if nothing else was received */
-  if (vnet_buffer (b)->tcp.data_len == 0)
-    tc->rcv_nxt += 1;
-  tcp_program_ack (wrk, tc);
-  tc->state = TCP_STATE_CLOSE_WAIT;
+  tcp_connection_set_state (tc, TCP_STATE_CLOSE_WAIT);
   tcp_program_disconnect (wrk, tc);
   tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME);
   TCP_EVT_DBG (TCP_EVT_FIN_RCVD, tc);
@@ -2080,7 +2093,6 @@ tcp46_established_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
   tcp_worker_ctx_t *wrk = tcp_get_worker (thread_index);
   u32 n_left_from, *from, *first_buffer;
   u16 err_counters[TCP_N_ERROR] = { 0 };
-  u8 is_fin = 0;
 
   if (node->flags & VLIB_NODE_FLAG_TRACE)
     tcp_established_trace_frame (vm, node, frame, is_ip4);
@@ -2092,7 +2104,7 @@ tcp46_established_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
     {
       u32 bi0, error0 = TCP_ERROR_ACK_OK;
       vlib_buffer_t *b0;
-      tcp_header_t *th0 = 0;
+      tcp_header_t *th0;
       tcp_connection_t *tc0;
 
       if (n_left_from > 1)
@@ -2118,13 +2130,6 @@ tcp46_established_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
        }
 
       th0 = tcp_buffer_hdr (b0);
-      /* N.B. buffer is rewritten if segment is ooo. Thus, th0 becomes a
-       * dangling reference. */
-      is_fin = tcp_is_fin (th0);
-
-      /* SYNs, FINs and data consume sequence numbers */
-      vnet_buffer (b0)->tcp.seq_end = vnet_buffer (b0)->tcp.seq_number
-       + tcp_is_syn (th0) + is_fin + vnet_buffer (b0)->tcp.data_len;
 
       /* TODO header prediction fast path */
 
@@ -2146,7 +2151,7 @@ tcp46_established_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
        error0 = tcp_segment_rcv (wrk, tc0, b0);
 
       /* 8: check the FIN bit */
-      if (PREDICT_FALSE (is_fin))
+      if (PREDICT_FALSE (tcp_is_fin (th0)))
        tcp_rcv_fin (wrk, tc0, b0, &error0);
 
     done:
@@ -2155,7 +2160,7 @@ tcp46_established_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 
   errors = session_manager_flush_enqueue_events (TRANSPORT_PROTO_TCP,
                                                 thread_index);
-  err_counters[TCP_ERROR_EVENT_FIFO_FULL] = errors;
+  err_counters[TCP_ERROR_MSG_QUEUE_FULL] = errors;
   tcp_store_err_counters (established, err_counters);
   tcp_handle_postponed_dequeues (wrk);
   tcp_handle_disconnects (wrk);
@@ -2368,10 +2373,8 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          goto drop;
        }
 
-      /* SYNs, FINs and data consume sequence numbers */
-      vnet_buffer (b0)->tcp.seq_end =
-       seq0 + tcp_is_syn (tcp0) + tcp_is_fin (tcp0) +
-       vnet_buffer (b0)->tcp.data_len;
+      /* SYNs consume sequence numbers */
+      vnet_buffer (b0)->tcp.seq_end += tcp_is_syn (tcp0);
 
       /*
        *  1. check the ACK bit
@@ -2434,7 +2437,7 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
        }
 
       /* Parse options */
-      if (tcp_options_parse (tcp0, &tc0->rcv_opts))
+      if (tcp_options_parse (tcp0, &tc0->rcv_opts, 1))
        {
          clib_warning ("options parse fail");
          error0 = TCP_ERROR_OPTIONS;
@@ -2449,7 +2452,7 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
       new_tc0->c_thread_index = my_thread_index;
       new_tc0->rcv_nxt = vnet_buffer (b0)->tcp.seq_end;
       new_tc0->irs = seq0;
-      new_tc0->timers[TCP_TIMER_ESTABLISH] = TCP_TIMER_HANDLE_INVALID;
+      new_tc0->timers[TCP_TIMER_ESTABLISH_AO] = TCP_TIMER_HANDLE_INVALID;
       new_tc0->timers[TCP_TIMER_RETRANSMIT_SYN] = TCP_TIMER_HANDLE_INVALID;
       new_tc0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
 
@@ -2466,6 +2469,8 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 
       if (tcp_opts_wscale (&new_tc0->rcv_opts))
        new_tc0->snd_wscale = new_tc0->rcv_opts.wscale;
+      else
+       new_tc0->rcv_wscale = 0;
 
       new_tc0->snd_wnd = clib_net_to_host_u16 (tcp0->window)
        << new_tc0->snd_wscale;
@@ -2553,7 +2558,7 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 
   errors = session_manager_flush_enqueue_events (TRANSPORT_PROTO_TCP,
                                                 my_thread_index);
-  tcp_inc_counter (syn_sent, TCP_ERROR_EVENT_FIFO_FULL, errors);
+  tcp_inc_counter (syn_sent, TCP_ERROR_MSG_QUEUE_FULL, errors);
   vlib_buffer_free (vm, first_buffer, from_frame->n_vectors);
 
   return from_frame->n_vectors;
@@ -2630,7 +2635,7 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 {
   u32 thread_index = vm->thread_index, errors = 0, *first_buffer;
   tcp_worker_ctx_t *wrk = tcp_get_worker (thread_index);
-  u32 n_left_from, *from;
+  u32 n_left_from, *from, max_dequeue;
 
   from = first_buffer = vlib_frame_vector_args (from_frame);
   n_left_from = from_frame->n_vectors;
@@ -2659,10 +2664,6 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
       tcp0 = tcp_buffer_hdr (b0);
       is_fin0 = tcp_is_fin (tcp0);
 
-      /* SYNs, FINs and data consume sequence numbers */
-      vnet_buffer (b0)->tcp.seq_end = vnet_buffer (b0)->tcp.seq_number
-       + tcp_is_syn (tcp0) + is_fin0 + vnet_buffer (b0)->tcp.data_len;
-
       if (CLIB_DEBUG)
        {
          tcp_connection_t *tmp;
@@ -2704,12 +2705,20 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
           */
          if (!tcp_rcv_ack_is_acceptable (tc0, b0))
            {
-             TCP_DBG ("connection not accepted");
-             tcp_send_reset_w_pkt (tc0, b0, is_ip4);
+             tcp_connection_reset (tc0);
              error0 = TCP_ERROR_ACK_INVALID;
              goto drop;
            }
 
+         /* Make sure the ack is exactly right */
+         if (tc0->rcv_nxt != vnet_buffer (b0)->tcp.seq_number || is_fin0
+             || vnet_buffer (b0)->tcp.data_len)
+           {
+             tcp_connection_reset (tc0);
+             error0 = TCP_ERROR_SEGMENT_INVALID;
+             goto drop;
+           }
+
          /* Update rtt and rto */
          tcp_estimate_initial_rtt (tc0);
 
@@ -2727,7 +2736,12 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          /* Reset SYN-ACK retransmit and SYN_RCV establish timers */
          tcp_retransmit_timer_reset (tc0);
          tcp_timer_reset (tc0, TCP_TIMER_ESTABLISH);
-         stream_session_accept_notify (&tc0->connection);
+         if (stream_session_accept_notify (&tc0->connection))
+           {
+             error0 = TCP_ERROR_MSG_QUEUE_FULL;
+             tcp_connection_reset (tc0);
+             goto drop;
+           }
          error0 = TCP_ERROR_ACK_OK;
          break;
        case TCP_STATE_ESTABLISHED:
@@ -2748,20 +2762,20 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          if (tc0->flags & TCP_CONN_FINPNDG)
            {
              /* TX fifo finally drained */
-             if (!session_tx_fifo_max_dequeue (&tc0->connection))
+             max_dequeue = session_tx_fifo_max_dequeue (&tc0->connection);
+             if (max_dequeue <= tc0->burst_acked)
                tcp_send_fin (tc0);
            }
          /* If FIN is ACKed */
          else if (tc0->snd_una == tc0->snd_una_max)
            {
-             tc0->state = TCP_STATE_FIN_WAIT_2;
-             TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
+             tcp_connection_set_state (tc0, TCP_STATE_FIN_WAIT_2);
 
              /* Stop all retransmit timers because we have nothing more
               * to send. Enable waitclose though because we're willing to
               * wait for peer's FIN but not indefinitely. */
              tcp_connection_timers_reset (tc0);
-             tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
+             tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
 
              /* Don't try to deq the FIN acked */
              if (tc0->burst_acked > 1)
@@ -2790,8 +2804,8 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
                {
                  tcp_send_fin (tc0);
                  tcp_connection_timers_reset (tc0);
-                 tc0->state = TCP_STATE_LAST_ACK;
-                 tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
+                 tcp_connection_set_state (tc0, TCP_STATE_LAST_ACK);
+                 tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
                }
            }
          break;
@@ -2802,9 +2816,9 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &error0))
            goto drop;
 
-         tc0->state = TCP_STATE_TIME_WAIT;
-         TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
-         tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME);
+         tcp_connection_timers_reset (tc0);
+         tcp_connection_set_state (tc0, TCP_STATE_TIME_WAIT);
+         tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME);
          goto drop;
 
          break;
@@ -2827,14 +2841,13 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
              goto drop;
            }
 
-         tc0->state = TCP_STATE_CLOSED;
-         TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
+         tcp_connection_set_state (tc0, TCP_STATE_CLOSED);
 
          /* Don't free the connection from the data path since
           * 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_update (tc0, TCP_TIMER_WAITCLOSE, 1);
+         tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME);
 
          goto drop;
 
@@ -2866,8 +2879,6 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
        case TCP_STATE_FIN_WAIT_2:
          if (vnet_buffer (b0)->tcp.data_len)
            error0 = tcp_segment_rcv (wrk, tc0, b0);
-         else if (is_fin0)
-           tc0->rcv_nxt += 1;
          break;
        case TCP_STATE_CLOSE_WAIT:
        case TCP_STATE_CLOSING:
@@ -2882,16 +2893,27 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
       if (!is_fin0)
        goto drop;
 
+      TCP_EVT_DBG (TCP_EVT_FIN_RCVD, tc0);
+
       switch (tc0->state)
        {
        case TCP_STATE_ESTABLISHED:
+         /* Account for the FIN and send ack */
+         tc0->rcv_nxt += 1;
+         tcp_program_ack (wrk, tc0);
+         tcp_connection_set_state (tc0, TCP_STATE_CLOSE_WAIT);
+         tcp_program_disconnect (wrk, tc0);
+         tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME);
+         break;
        case TCP_STATE_SYN_RCVD:
-         /* Send FIN-ACK notify app and enter CLOSE-WAIT */
+         /* Send FIN-ACK, enter LAST-ACK and because the app was not
+          * notified yet, set a cleanup timer instead of relying on
+          * disconnect notify and the implicit close call. */
          tcp_connection_timers_reset (tc0);
+         tc0->rcv_nxt += 1;
          tcp_send_fin (tc0);
-         stream_session_disconnect_notify (&tc0->connection);
-         tc0->state = TCP_STATE_CLOSE_WAIT;
-         TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
+         tcp_connection_set_state (tc0, TCP_STATE_LAST_ACK);
+         tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
          break;
        case TCP_STATE_CLOSE_WAIT:
        case TCP_STATE_CLOSING:
@@ -2899,19 +2921,19 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          /* move along .. */
          break;
        case TCP_STATE_FIN_WAIT_1:
-         tc0->state = TCP_STATE_CLOSING;
+         tc0->rcv_nxt += 1;
+         tcp_connection_set_state (tc0, TCP_STATE_CLOSING);
          tcp_program_ack (wrk, tc0);
-         TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
          /* Wait for ACK but not forever */
          tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
          break;
        case TCP_STATE_FIN_WAIT_2:
          /* Got FIN, send ACK! Be more aggressive with resource cleanup */
-         tc0->state = TCP_STATE_TIME_WAIT;
+         tc0->rcv_nxt += 1;
+         tcp_connection_set_state (tc0, TCP_STATE_TIME_WAIT);
          tcp_connection_timers_reset (tc0);
-         tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME);
+         tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME);
          tcp_program_ack (wrk, tc0);
-         TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
          break;
        case TCP_STATE_TIME_WAIT:
          /* Remain in the TIME-WAIT state. Restart the time-wait
@@ -2921,7 +2943,6 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          break;
        }
       error0 = TCP_ERROR_FIN_RCVD;
-      TCP_EVT_DBG (TCP_EVT_FIN_RCVD, tc0);
 
     drop:
 
@@ -2935,7 +2956,7 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 
   errors = session_manager_flush_enqueue_events (TRANSPORT_PROTO_TCP,
                                                 thread_index);
-  tcp_inc_counter (rcv_process, TCP_ERROR_EVENT_FIFO_FULL, errors);
+  tcp_inc_counter (rcv_process, TCP_ERROR_MSG_QUEUE_FULL, errors);
   tcp_handle_postponed_dequeues (wrk);
   vlib_buffer_free (vm, first_buffer, from_frame->n_vectors);
 
@@ -3073,7 +3094,7 @@ tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
        }
 
       /* Create child session and send SYN-ACK */
-      child0 = tcp_connection_new (my_thread_index);
+      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;
@@ -3093,9 +3114,10 @@ tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
                            sizeof (ip6_address_t));
        }
 
-      if (tcp_options_parse (th0, &child0->rcv_opts))
+      if (tcp_options_parse (th0, &child0->rcv_opts, 1))
        {
-         clib_warning ("options parse fail");
+         error0 = TCP_ERROR_OPTIONS;
+         tcp_connection_free (child0);
          goto drop;
        }
 
@@ -3126,7 +3148,6 @@ tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
       if (stream_session_accept (&child0->connection, lc0->c_s_index,
                                 0 /* notify */ ))
        {
-         clib_warning ("session accept fail");
          tcp_connection_cleanup (child0);
          error0 = TCP_ERROR_CREATE_SESSION_FAIL;
          goto drop;
@@ -3302,13 +3323,19 @@ tcp_input_lookup_buffer (vlib_buffer_t * b, u8 thread_index, u32 * error,
   if (is_ip4)
     {
       ip4_header_t *ip4 = vlib_buffer_get_current (b);
+      int ip_hdr_bytes = ip4_header_bytes (ip4);
+      if (PREDICT_FALSE (b->current_length < ip_hdr_bytes + sizeof (*tcp)))
+       {
+         *error = TCP_ERROR_LENGTH;
+         return 0;
+       }
       tcp = ip4_next_header (ip4);
       vnet_buffer (b)->tcp.hdr_offset = (u8 *) tcp - (u8 *) ip4;
-      n_advance_bytes = (ip4_header_bytes (ip4) + tcp_header_bytes (tcp));
+      n_advance_bytes = (ip_hdr_bytes + tcp_header_bytes (tcp));
       n_data_bytes = clib_net_to_host_u16 (ip4->length) - n_advance_bytes;
 
       /* Length check. Checksum computed by ipx_local no need to compute again */
-      if (PREDICT_FALSE (n_advance_bytes < 0))
+      if (PREDICT_FALSE (n_data_bytes < 0))
        {
          *error = TCP_ERROR_LENGTH;
          return 0;
@@ -3322,6 +3349,11 @@ tcp_input_lookup_buffer (vlib_buffer_t * b, u8 thread_index, u32 * error,
   else
     {
       ip6_header_t *ip6 = vlib_buffer_get_current (b);
+      if (PREDICT_FALSE (b->current_length < sizeof (*ip6) + sizeof (*tcp)))
+       {
+         *error = TCP_ERROR_LENGTH;
+         return 0;
+       }
       tcp = ip6_next_header (ip6);
       vnet_buffer (b)->tcp.hdr_offset = (u8 *) tcp - (u8 *) ip6;
       n_advance_bytes = tcp_header_bytes (tcp);
@@ -3329,7 +3361,7 @@ tcp_input_lookup_buffer (vlib_buffer_t * b, u8 thread_index, u32 * error,
        - n_advance_bytes;
       n_advance_bytes += sizeof (ip6[0]);
 
-      if (PREDICT_FALSE (n_advance_bytes < 0))
+      if (PREDICT_FALSE (n_data_bytes < 0))
        {
          *error = TCP_ERROR_LENGTH;
          return 0;
@@ -3345,6 +3377,8 @@ tcp_input_lookup_buffer (vlib_buffer_t * b, u8 thread_index, u32 * error,
   vnet_buffer (b)->tcp.ack_number = clib_net_to_host_u32 (tcp->ack_number);
   vnet_buffer (b)->tcp.data_offset = n_advance_bytes;
   vnet_buffer (b)->tcp.data_len = n_data_bytes;
+  vnet_buffer (b)->tcp.seq_end = vnet_buffer (b)->tcp.seq_number
+    + n_data_bytes;
   vnet_buffer (b)->tcp.flags = 0;
 
   *error = is_filtered ? TCP_ERROR_FILTERED : *error;
@@ -3372,8 +3406,9 @@ tcp_input_dispatch_buffer (tcp_main_t * tm, tcp_connection_t * tc,
       vnet_buffer (b)->tcp.flags = tc->state;
 
       if (*error == TCP_ERROR_DISPATCH)
-       clib_warning ("disp error state %U flags %U", format_tcp_state,
-                     state, format_tcp_flags, (int) flags);
+       clib_warning ("tcp conn %u disp error state %U flags %U",
+                     tc->c_c_index, format_tcp_state, state,
+                     format_tcp_flags, (int) flags);
     }
 }
 
@@ -3560,20 +3595,62 @@ do {                                                            \
     tm->dispatch_table[TCP_STATE_##t][f].error = (e);          \
 } while (0)
 
-  /* SYNs for new connections -> tcp-listen. */
+  /* RFC 793: In LISTEN if RST drop and if ACK return RST */
+  _(LISTEN, 0, TCP_INPUT_NEXT_DROP, TCP_ERROR_SEGMENT_INVALID);
+  _(LISTEN, TCP_FLAG_ACK, TCP_INPUT_NEXT_RESET, TCP_ERROR_ACK_INVALID);
+  _(LISTEN, TCP_FLAG_RST, TCP_INPUT_NEXT_DROP, TCP_ERROR_INVALID_CONNECTION);
   _(LISTEN, TCP_FLAG_SYN, TCP_INPUT_NEXT_LISTEN, TCP_ERROR_NONE);
-  _(LISTEN, TCP_FLAG_ACK, TCP_INPUT_NEXT_RESET, TCP_ERROR_NONE);
-  _(LISTEN, TCP_FLAG_RST, TCP_INPUT_NEXT_DROP, TCP_ERROR_RST_RCVD);
+  _(LISTEN, TCP_FLAG_SYN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RESET,
+    TCP_ERROR_ACK_INVALID);
+  _(LISTEN, TCP_FLAG_SYN | TCP_FLAG_RST, TCP_INPUT_NEXT_DROP,
+    TCP_ERROR_SEGMENT_INVALID);
+  _(LISTEN, TCP_FLAG_SYN | TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_DROP,
+    TCP_ERROR_SEGMENT_INVALID);
+  _(LISTEN, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_DROP,
+    TCP_ERROR_INVALID_CONNECTION);
+  _(LISTEN, TCP_FLAG_FIN, TCP_INPUT_NEXT_RESET, TCP_ERROR_SEGMENT_INVALID);
   _(LISTEN, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RESET,
+    TCP_ERROR_SEGMENT_INVALID);
+  _(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);
+  _(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,
+    TCP_ERROR_SEGMENT_INVALID);
+  _(LISTEN, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_RST, TCP_INPUT_NEXT_DROP,
+    TCP_ERROR_SEGMENT_INVALID);
+  _(LISTEN, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_RST | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_DROP, TCP_ERROR_SEGMENT_INVALID);
   /* ACK for for a SYN-ACK -> tcp-rcv-process. */
   _(SYN_RCVD, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(SYN_RCVD, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(SYN_RCVD, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
     TCP_ERROR_NONE);
   _(SYN_RCVD, TCP_FLAG_SYN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(SYN_RCVD, TCP_FLAG_SYN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(SYN_RCVD, TCP_FLAG_SYN | TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(SYN_RCVD, TCP_FLAG_SYN | TCP_FLAG_RST | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(SYN_RCVD, TCP_FLAG_FIN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(SYN_RCVD, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
     TCP_ERROR_NONE);
+  _(SYN_RCVD, TCP_FLAG_FIN | TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(SYN_RCVD, TCP_FLAG_FIN | TCP_FLAG_RST | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(SYN_RCVD, TCP_FLAG_FIN | TCP_FLAG_SYN, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(SYN_RCVD, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_RST,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(SYN_RCVD, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(SYN_RCVD, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_RST | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(SYN_RCVD, 0, TCP_INPUT_NEXT_DROP, TCP_ERROR_SEGMENT_INVALID);
   /* SYN-ACK for a SYN */
   _(SYN_SENT, TCP_FLAG_SYN | TCP_FLAG_ACK, TCP_INPUT_NEXT_SYN_SENT,
     TCP_ERROR_NONE);
@@ -3587,43 +3664,123 @@ do {                                                           \
   _(ESTABLISHED, TCP_FLAG_FIN, TCP_INPUT_NEXT_ESTABLISHED, TCP_ERROR_NONE);
   _(ESTABLISHED, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_ESTABLISHED,
     TCP_ERROR_NONE);
+  _(ESTABLISHED, TCP_FLAG_FIN | TCP_FLAG_RST, TCP_INPUT_NEXT_ESTABLISHED,
+    TCP_ERROR_NONE);
+  _(ESTABLISHED, TCP_FLAG_FIN | TCP_FLAG_RST | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_ESTABLISHED, TCP_ERROR_NONE);
+  _(ESTABLISHED, TCP_FLAG_FIN | TCP_FLAG_SYN, TCP_INPUT_NEXT_ESTABLISHED,
+    TCP_ERROR_NONE);
+  _(ESTABLISHED, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_ESTABLISHED, TCP_ERROR_NONE);
+  _(ESTABLISHED, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_RST,
+    TCP_INPUT_NEXT_ESTABLISHED, TCP_ERROR_NONE);
+  _(ESTABLISHED, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_RST | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_ESTABLISHED, TCP_ERROR_NONE);
   _(ESTABLISHED, TCP_FLAG_RST, TCP_INPUT_NEXT_ESTABLISHED, TCP_ERROR_NONE);
   _(ESTABLISHED, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_ESTABLISHED,
     TCP_ERROR_NONE);
   _(ESTABLISHED, TCP_FLAG_SYN, TCP_INPUT_NEXT_ESTABLISHED, TCP_ERROR_NONE);
   _(ESTABLISHED, TCP_FLAG_SYN | TCP_FLAG_ACK, TCP_INPUT_NEXT_ESTABLISHED,
     TCP_ERROR_NONE);
+  _(ESTABLISHED, TCP_FLAG_SYN | TCP_FLAG_RST, TCP_INPUT_NEXT_ESTABLISHED,
+    TCP_ERROR_NONE);
+  _(ESTABLISHED, TCP_FLAG_SYN | TCP_FLAG_RST | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_ESTABLISHED, TCP_ERROR_NONE);
+  _(ESTABLISHED, 0, TCP_INPUT_NEXT_DROP, TCP_ERROR_SEGMENT_INVALID);
   /* ACK or FIN-ACK to our FIN */
   _(FIN_WAIT_1, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(FIN_WAIT_1, TCP_FLAG_ACK | TCP_FLAG_FIN, TCP_INPUT_NEXT_RCV_PROCESS,
     TCP_ERROR_NONE);
   /* FIN in reply to our FIN from the other side */
+  _(FIN_WAIT_1, 0, TCP_INPUT_NEXT_DROP, TCP_ERROR_SEGMENT_INVALID);
   _(FIN_WAIT_1, TCP_FLAG_FIN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(FIN_WAIT_1, TCP_FLAG_FIN | TCP_FLAG_SYN, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(FIN_WAIT_1, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(FIN_WAIT_1, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_RST,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(FIN_WAIT_1, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_RST | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(FIN_WAIT_1, TCP_FLAG_FIN | TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(FIN_WAIT_1, TCP_FLAG_FIN | TCP_FLAG_RST | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(FIN_WAIT_1, TCP_FLAG_SYN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(FIN_WAIT_1, TCP_FLAG_SYN | TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(FIN_WAIT_1, TCP_FLAG_SYN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(FIN_WAIT_1, TCP_FLAG_SYN | TCP_FLAG_RST | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(FIN_WAIT_1, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(FIN_WAIT_1, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
   _(CLOSING, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(CLOSING, TCP_FLAG_SYN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(CLOSING, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(CLOSING, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(CLOSING, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
   /* FIN confirming that the peer (app) has closed */
   _(FIN_WAIT_2, TCP_FLAG_FIN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(FIN_WAIT_2, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(FIN_WAIT_2, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
     TCP_ERROR_NONE);
+  _(FIN_WAIT_2, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(FIN_WAIT_2, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
   _(CLOSE_WAIT, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(CLOSE_WAIT, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
     TCP_ERROR_NONE);
+  _(CLOSE_WAIT, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(CLOSE_WAIT, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(LAST_ACK, 0, TCP_INPUT_NEXT_DROP, TCP_ERROR_SEGMENT_INVALID);
   _(LAST_ACK, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(LAST_ACK, TCP_FLAG_FIN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(LAST_ACK, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
     TCP_ERROR_NONE);
+  _(LAST_ACK, TCP_FLAG_FIN | TCP_FLAG_SYN, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(LAST_ACK, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(LAST_ACK, TCP_FLAG_FIN | TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(LAST_ACK, TCP_FLAG_FIN | TCP_FLAG_RST | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(LAST_ACK, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_RST,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(LAST_ACK, TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_RST | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(LAST_ACK, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(LAST_ACK, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
   _(LAST_ACK, TCP_FLAG_SYN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(LAST_ACK, TCP_FLAG_SYN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(LAST_ACK, TCP_FLAG_SYN | TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
+  _(LAST_ACK, TCP_FLAG_SYN | TCP_FLAG_RST | TCP_FLAG_ACK,
+    TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(TIME_WAIT, TCP_FLAG_FIN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(TIME_WAIT, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
     TCP_ERROR_NONE);
   _(TIME_WAIT, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(TIME_WAIT, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
   _(TIME_WAIT, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
-  _(CLOSED, TCP_FLAG_ACK, TCP_INPUT_NEXT_DROP, TCP_ERROR_CONNECTION_CLOSED);
+  /* RFC793 CLOSED: An incoming segment containing a RST is discarded. An
+   * incoming segment not containing a RST causes a RST to be sent in
+   * response.*/
   _(CLOSED, TCP_FLAG_RST, TCP_INPUT_NEXT_DROP, TCP_ERROR_CONNECTION_CLOSED);
-  _(CLOSED, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_DROP,
+  _(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_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RESET,
+    TCP_ERROR_NONE);
 #undef _
 }