X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvnet%2Ftcp%2Ftcp_input.c;h=b27e0b37d7d6ae783fd9c2929f90c2c833c5296f;hb=3c514d5516498c67f313d3bfe62288d4f36fba54;hp=9c303eb01a5fb58f31fedc4a2338853914b3e607;hpb=7ac053b27fee8f9e437cf7b61357943356381061;p=vpp.git diff --git a/src/vnet/tcp/tcp_input.c b/src/vnet/tcp/tcp_input.c index 9c303eb01a5..b27e0b37d7d 100644 --- a/src/vnet/tcp/tcp_input.c +++ b/src/vnet/tcp/tcp_input.c @@ -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 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); } @@ -455,8 +457,11 @@ tcp_update_rtt (tcp_connection_t * tc, u32 ack) if (tc->rtt_ts && seq_geq (ack, tc->rtt_seq)) { - tc->mrtt_us = tcp_time_now_us (tc->c_thread_index) - tc->rtt_ts; - mrtt = clib_max ((u32) (tc->mrtt_us * THZ), 1); + f64 sample = tcp_time_now_us (tc->c_thread_index) - tc->rtt_ts; + tc->mrtt_us = tc->mrtt_us + (sample - tc->mrtt_us) * 0.125; + mrtt = clib_max ((u32) (sample * THZ), 1); + /* Allow measuring of a new RTT */ + tc->rtt_ts = 0; } /* As per RFC7323 TSecr can be used for RTTM only if the segment advances * snd_una, i.e., the left side of the send window: @@ -475,9 +480,6 @@ tcp_update_rtt (tcp_connection_t * tc, u32 ack) done: - /* Allow measuring of a new RTT */ - tc->rtt_ts = 0; - /* If we got here something must've been ACKed so make sure boff is 0, * even if mrtt is not valid since we update the rto lower */ tc->rto_boff = 0; @@ -486,6 +488,30 @@ done: return 0; } +static void +tcp_estimate_initial_rtt (tcp_connection_t * tc) +{ + u8 thread_index = vlib_num_workers ()? 1 : 0; + int mrtt; + + if (tc->rtt_ts) + { + tc->mrtt_us = tcp_time_now_us (thread_index) - tc->rtt_ts; + mrtt = clib_max ((u32) (tc->mrtt_us * THZ), 1); + tc->rtt_ts = 0; + } + else + { + mrtt = tcp_time_now_w_thread (thread_index) - tc->rcv_opts.tsecr; + mrtt = clib_max (mrtt, 1); + tc->mrtt_us = (f64) mrtt *TCP_TICK; + } + + if (mrtt > 0 && mrtt < TCP_RTT_MAX) + tcp_estimate_rtt (tc, mrtt); + tcp_update_rto (tc); +} + /** * Dequeue bytes for connections that have received acks in last burst */ @@ -506,14 +532,28 @@ tcp_handle_postponed_dequeues (tcp_worker_ctx_t * wrk) tc = tcp_connection_get (pending_deq_acked[i], thread_index); tc->flags &= ~TCP_CONN_DEQ_PENDING; + if (PREDICT_FALSE (!tc->burst_acked)) + continue; + /* Dequeue the newly ACKed bytes */ stream_session_dequeue_drop (&tc->connection, tc->burst_acked); 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); + + /* If not congested, update pacer based on our new + * cwnd estimate */ + if (!tcp_in_fastrecovery (tc)) + tcp_connection_tx_pacer_update (tc); } _vec_len (wrk->pending_deq_acked) = 0; } @@ -868,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; @@ -1084,6 +1126,7 @@ tcp_cc_recovery_exit (tcp_connection_t * tc) tcp_update_rto (tc); tc->snd_rxt_ts = 0; tc->snd_nxt = tc->snd_una_max; + tc->rtt_ts = 0; tcp_recovery_off (tc); TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 3); } @@ -1096,9 +1139,9 @@ tcp_cc_fastrecovery_exit (tcp_connection_t * tc) tc->rcv_dupacks = 0; tc->snd_nxt = tc->snd_una_max; tc->snd_rxt_bytes = 0; + tc->rtt_ts = 0; tcp_fastrecovery_off (tc); - tcp_fastrecovery_1_smss_off (tc); tcp_fastrecovery_first_off (tc); TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 3); @@ -1381,6 +1424,10 @@ partial_ack: * Legitimate ACK. 1) See if we can exit recovery */ + /* Update the pacing rate. For the first partial ack we move from + * the artificially constrained rate to the one after congestion */ + tcp_connection_tx_pacer_update (tc); + if (seq_geq (tc->snd_una, tc->snd_congestion)) { tcp_retransmit_timer_update (tc); @@ -1403,12 +1450,8 @@ partial_ack: * Legitimate ACK. 2) If PARTIAL ACK try to retransmit */ - /* Update the pacing rate. For the first partial ack we move from - * the artificially constrained rate to the one after congestion */ - tcp_connection_tx_pacer_update (tc); - /* XXX limit this only to first partial ack? */ - tcp_retransmit_timer_force_update (tc); + tcp_retransmit_timer_update (tc); /* RFC6675: If the incoming ACK is a cumulative acknowledgment, * reset dupacks to 0. Also needed if in congestion recovery */ @@ -1552,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; @@ -1567,6 +1613,53 @@ process_ack: return 0; } +static void +tcp_program_disconnect (tcp_worker_ctx_t * wrk, tcp_connection_t * tc) +{ + if (!tcp_disconnect_pending (tc)) + { + vec_add1 (wrk->pending_disconnects, tc->c_c_index); + tcp_disconnect_pending_on (tc); + } +} + +static void +tcp_handle_disconnects (tcp_worker_ctx_t * wrk) +{ + u32 thread_index, *pending_disconnects; + tcp_connection_t *tc; + int i; + + if (!vec_len (wrk->pending_disconnects)) + return; + + 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); + stream_session_disconnect_notify (&tc->connection); + } + _vec_len (wrk->pending_disconnects) = 0; +} + +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). */ + 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); + *error = TCP_ERROR_FIN_RCVD; +} + static u8 tcp_sack_vector_is_sane (sack_block_t * sacks) { @@ -1910,13 +2003,14 @@ tcp_set_rx_trace_data (tcp_rx_trace_t * t0, tcp_connection_t * tc0, { if (tc0) { - clib_memcpy (&t0->tcp_connection, tc0, sizeof (t0->tcp_connection)); + clib_memcpy_fast (&t0->tcp_connection, tc0, + sizeof (t0->tcp_connection)); } else { th0 = tcp_buffer_hdr (b0); } - clib_memcpy (&t0->tcp_header, th0, sizeof (t0->tcp_header)); + clib_memcpy_fast (&t0->tcp_header, th0, sizeof (t0->tcp_header)); } static void @@ -1999,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); @@ -2011,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) @@ -2037,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 */ @@ -2065,20 +2151,8 @@ 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)) - { - /* 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 (b0)->tcp.data_len == 0) - tc0->rcv_nxt += 1; - tcp_program_ack (wrk, tc0); - tc0->state = TCP_STATE_CLOSE_WAIT; - stream_session_disconnect_notify (&tc0->connection); - tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME); - TCP_EVT_DBG (TCP_EVT_FIN_RCVD, tc0); - error0 = TCP_ERROR_FIN_RCVD; - } + if (PREDICT_FALSE (tcp_is_fin (th0))) + tcp_rcv_fin (wrk, tc0, b0, &error0); done: tcp_inc_err_counter (err_counters, error0, 1); @@ -2086,9 +2160,10 @@ 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); vlib_buffer_free (vm, first_buffer, frame->n_vectors); return frame->n_vectors; @@ -2298,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 @@ -2364,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; @@ -2374,7 +2447,7 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node, /* Valid SYN or SYN-ACK. Move connection from half-open pool to * current thread pool. */ pool_get (tm->connections[my_thread_index], new_tc0); - clib_memcpy (new_tc0, tc0, sizeof (*new_tc0)); + clib_memcpy_fast (new_tc0, tc0, sizeof (*new_tc0)); new_tc0->c_c_index = new_tc0 - tm->connections[my_thread_index]; new_tc0->c_thread_index = my_thread_index; new_tc0->rcv_nxt = vnet_buffer (b0)->tcp.seq_end; @@ -2396,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; @@ -2426,8 +2501,10 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node, goto drop; } + new_tc0->tx_fifo_size = + transport_tx_fifo_size (&new_tc0->connection); /* Update rtt with the syn-ack sample */ - tcp_update_rtt (new_tc0, vnet_buffer (b0)->tcp.ack_number); + tcp_estimate_initial_rtt (new_tc0); TCP_EVT_DBG (TCP_EVT_SYNACK_RCVD, new_tc0); error0 = TCP_ERROR_SYN_ACKS_RCVD; } @@ -2445,8 +2522,10 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node, goto drop; } - tc0->rtt_ts = 0; - tcp_init_snd_vars (tc0); + new_tc0->tx_fifo_size = + transport_tx_fifo_size (&new_tc0->connection); + new_tc0->rtt_ts = 0; + tcp_init_snd_vars (new_tc0); tcp_send_synack (new_tc0); error0 = TCP_ERROR_SYNS_RCVD; goto drop; @@ -2471,14 +2550,15 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node, if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED) && tcp0 != 0)) { t0 = vlib_add_trace (vm, node, b0, sizeof (*t0)); - clib_memcpy (&t0->tcp_header, tcp0, sizeof (t0->tcp_header)); - clib_memcpy (&t0->tcp_connection, tc0, sizeof (t0->tcp_connection)); + clib_memcpy_fast (&t0->tcp_header, tcp0, sizeof (t0->tcp_header)); + clib_memcpy_fast (&t0->tcp_connection, tc0, + sizeof (t0->tcp_connection)); } } 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; @@ -2555,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; @@ -2584,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; @@ -2629,14 +2705,22 @@ 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_update_rtt (tc0, vnet_buffer (b0)->tcp.ack_number); + tcp_estimate_initial_rtt (tc0); /* Switch state to ESTABLISHED */ tc0->state = TCP_STATE_ESTABLISHED; @@ -2652,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: @@ -2673,20 +2762,26 @@ 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); + + /* Don't try to deq the FIN acked */ + if (tc0->burst_acked > 1) + stream_session_dequeue_drop (&tc0->connection, + tc0->burst_acked - 1); + tc0->burst_acked = 0; } break; case TCP_STATE_FIN_WAIT_2: @@ -2695,6 +2790,7 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, * acknowledged ("ok") but do not delete the TCB. */ if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &error0)) goto drop; + tc0->burst_acked = 0; break; case TCP_STATE_CLOSE_WAIT: /* Do the same processing as for the ESTABLISHED state. */ @@ -2708,7 +2804,7 @@ 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_connection_set_state (tc0, TCP_STATE_LAST_ACK); tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME); } } @@ -2720,8 +2816,7 @@ 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_connection_set_state (tc0, TCP_STATE_TIME_WAIT); tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME); goto drop; @@ -2745,14 +2840,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_update (tc0, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME); goto drop; @@ -2784,8 +2878,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: @@ -2800,16 +2892,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: @@ -2817,19 +2920,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_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 @@ -2839,7 +2942,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: @@ -2853,7 +2955,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); @@ -2991,7 +3093,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; @@ -3005,15 +3107,16 @@ tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node, } else { - clib_memcpy (&child0->c_lcl_ip6, &ip60->dst_address, - sizeof (ip6_address_t)); - clib_memcpy (&child0->c_rmt_ip6, &ip60->src_address, - sizeof (ip6_address_t)); + 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)); } - 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; } @@ -3044,12 +3147,12 @@ 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; } + child0->tx_fifo_size = transport_tx_fifo_size (&child0->connection); tcp_send_synack (child0); tcp_timer_set (child0, TCP_TIMER_ESTABLISH, TCP_SYN_RCVD_TIME); @@ -3058,8 +3161,9 @@ tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node, if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) { t0 = vlib_add_trace (vm, node, b0, sizeof (*t0)); - clib_memcpy (&t0->tcp_header, th0, sizeof (t0->tcp_header)); - clib_memcpy (&t0->tcp_connection, lc0, sizeof (t0->tcp_connection)); + clib_memcpy_fast (&t0->tcp_header, th0, sizeof (t0->tcp_header)); + clib_memcpy_fast (&t0->tcp_connection, lc0, + sizeof (t0->tcp_connection)); } n_syns += (error0 == TCP_ERROR_NONE); @@ -3218,13 +3322,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; @@ -3238,6 +3348,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); @@ -3245,7 +3360,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; @@ -3261,6 +3376,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; @@ -3288,8 +3405,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); } } @@ -3476,20 +3594,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); @@ -3503,43 +3663,85 @@ 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, TCP_FLAG_FIN, 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_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, 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_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); _(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 _ }