tcp/session: notify transport of close when tx fifo is not empty
[vpp.git] / src / vnet / tcp / tcp_input.c
index d1736e9..4e00526 100644 (file)
@@ -183,11 +183,7 @@ tcp_options_parse (tcp_header_t * th, tcp_options_t * to, u8 is_syn)
              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:
@@ -245,7 +241,7 @@ tcp_options_parse (tcp_header_t * th, tcp_options_t * to, u8 is_syn)
 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);
 }
 
@@ -307,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);
 
@@ -317,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 */
@@ -546,6 +539,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);
@@ -908,7 +907,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;
@@ -1593,7 +1594,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;
@@ -2164,7 +2168,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);
@@ -2475,6 +2479,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;
@@ -2562,7 +2568,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;
@@ -2713,12 +2719,19 @@ 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)
+           {
+             tcp_connection_reset (tc0);
+             error0 = TCP_ERROR_SEGMENT_INVALID;
+             goto drop;
+           }
+
          /* Update rtt and rto */
          tcp_estimate_initial_rtt (tc0);
 
@@ -2736,7 +2749,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:
@@ -2843,7 +2861,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_update (tc0, TCP_TIMER_WAITCLOSE, 1);
+         tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME);
 
          goto drop;
 
@@ -2900,6 +2918,7 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          tcp_send_fin (tc0);
          stream_session_disconnect_notify (&tc0->connection);
          tc0->state = TCP_STATE_CLOSE_WAIT;
+         tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME);
          TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
          break;
        case TCP_STATE_CLOSE_WAIT:
@@ -2944,7 +2963,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);
 
@@ -3082,7 +3101,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;
@@ -3104,7 +3123,8 @@ tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 
       if (tcp_options_parse (th0, &child0->rcv_opts, 1))
        {
-         clib_warning ("options parse fail");
+         error0 = TCP_ERROR_OPTIONS;
+         tcp_connection_free (child0);
          goto drop;
        }
 
@@ -3135,7 +3155,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;
@@ -3392,8 +3411,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);
     }
 }
 
@@ -3620,6 +3640,8 @@ do {                                                              \
   /* FIN in reply to our FIN from the other side */
   _(FIN_WAIT_1, TCP_FLAG_FIN, 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);
   /* FIN confirming that the peer (app) has closed */
   _(FIN_WAIT_2, TCP_FLAG_FIN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);