tcp_connection_t tcp_connection;
} tcp_tx_trace_t;
-u16 dummy_mtu = 400;
+u16 dummy_mtu = 1460;
u8 *
format_tcp_tx_trace (u8 * s, va_list * args)
s = format (s, "%U\n%U%U",
format_tcp_header, &t->tcp_header, 128,
format_white_space, indent,
- format_tcp_connection_verbose, &t->tcp_connection);
+ format_tcp_connection, &t->tcp_connection, 1);
return s;
}
* Figure out how much space we have available
*/
available_space = stream_session_max_rx_enqueue (&tc->connection);
- max_fifo = stream_session_fifo_size (&tc->connection);
+ max_fifo = stream_session_rx_fifo_size (&tc->connection);
- ASSERT (tc->opt.mss < max_fifo);
- if (available_space < tc->opt.mss && available_space < max_fifo >> 3)
+ ASSERT (tc->rcv_opts.mss < max_fifo);
+ if (available_space < tc->rcv_opts.mss && available_space < max_fifo >> 3)
available_space = 0;
/*
opts->tsecr = 0;
len += TCP_OPTION_LEN_TIMESTAMP;
- opts->flags |= TCP_OPTS_FLAG_SACK_PERMITTED;
- len += TCP_OPTION_LEN_SACK_PERMITTED;
+ if (TCP_USE_SACKS)
+ {
+ opts->flags |= TCP_OPTS_FLAG_SACK_PERMITTED;
+ len += TCP_OPTION_LEN_SACK_PERMITTED;
+ }
/* Align to needed boundary */
len += (TCP_OPTS_ALIGN - len % TCP_OPTS_ALIGN) % TCP_OPTS_ALIGN;
opts->mss = tc->mss;
len += TCP_OPTION_LEN_MSS;
- if (tcp_opts_wscale (&tc->opt))
+ if (tcp_opts_wscale (&tc->rcv_opts))
{
opts->flags |= TCP_OPTS_FLAG_WSCALE;
opts->wscale = tc->rcv_wscale;
len += TCP_OPTION_LEN_WINDOW_SCALE;
}
- if (tcp_opts_tstamp (&tc->opt))
+ if (tcp_opts_tstamp (&tc->rcv_opts))
{
opts->flags |= TCP_OPTS_FLAG_TSTAMP;
opts->tsval = tcp_time_now ();
len += TCP_OPTION_LEN_TIMESTAMP;
}
- if (tcp_opts_sack_permitted (&tc->opt))
+ if (tcp_opts_sack_permitted (&tc->rcv_opts))
{
opts->flags |= TCP_OPTS_FLAG_SACK_PERMITTED;
len += TCP_OPTION_LEN_SACK_PERMITTED;
opts->flags = 0;
- if (tcp_opts_tstamp (&tc->opt))
+ if (tcp_opts_tstamp (&tc->rcv_opts))
{
opts->flags |= TCP_OPTS_FLAG_TSTAMP;
opts->tsval = tcp_time_now ();
opts->tsecr = tc->tsval_recent;
len += TCP_OPTION_LEN_TIMESTAMP;
}
- if (tcp_opts_sack_permitted (&tc->opt))
+ if (tcp_opts_sack_permitted (&tc->rcv_opts))
{
if (vec_len (tc->snd_sacks))
{
tcp_make_options (tc, &tc->snd_opts, TCP_STATE_ESTABLISHED);
/* XXX check if MTU has been updated */
- tc->snd_mss = clib_min (tc->mss, tc->opt.mss) - tc->snd_opts_len;
+ tc->snd_mss = clib_min (tc->mss, tc->rcv_opts.mss) - tc->snd_opts_len;
+ ASSERT (tc->snd_mss > 0);
}
void
tcp_init_mss (tcp_connection_t * tc)
{
+ u16 default_min_mss = 536;
tcp_update_rcv_mss (tc);
/* TODO cache mss and consider PMTU discovery */
- tc->snd_mss = clib_min (tc->opt.mss, tc->mss);
+ tc->snd_mss = clib_min (tc->rcv_opts.mss, tc->mss);
- if (tc->snd_mss == 0)
+ if (tc->snd_mss < 45)
{
clib_warning ("snd mss is 0");
- tc->snd_mss = tc->mss;
+ /* Assume that at least the min default mss works */
+ tc->snd_mss = default_min_mss;
+ tc->rcv_opts.mss = default_min_mss;
}
/* We should have enough space for 40 bytes of options */
ASSERT (tc->snd_mss > 45);
/* If we use timestamp option, account for it */
- if (tcp_opts_tstamp (&tc->opt))
+ if (tcp_opts_tstamp (&tc->rcv_opts))
tc->snd_mss -= TCP_OPTION_LEN_TIMESTAMP;
}
int
tcp_make_reset_in_place (vlib_main_t * vm, vlib_buffer_t * b0,
- tcp_state_t state, u32 my_thread_index, u8 is_ip4)
+ tcp_state_t state, u8 thread_index, u8 is_ip4)
{
- u8 tcp_hdr_len = sizeof (tcp_header_t);
ip4_header_t *ih4;
ip6_header_t *ih6;
tcp_header_t *th0;
- ip4_address_t src_ip40;
- ip6_address_t src_ip60;
- u16 src_port0;
+ ip4_address_t src_ip40, dst_ip40;
+ ip6_address_t src_ip60, dst_ip60;
+ u16 src_port, dst_port;
u32 tmp;
+ u32 seq, ack;
+ u8 flags;
/* Find IP and TCP headers */
- if (is_ip4)
- {
- ih4 = vlib_buffer_get_current (b0);
- th0 = ip4_next_header (ih4);
- }
- else
- {
- ih6 = vlib_buffer_get_current (b0);
- th0 = ip6_next_header (ih6);
- }
+ th0 = tcp_buffer_hdr (b0);
- /* Swap src and dst ip */
+ /* Save src and dst ip */
if (is_ip4)
{
+ ih4 = vlib_buffer_get_current (b0);
ASSERT ((ih4->ip_version_and_header_length & 0xF0) == 0x40);
src_ip40.as_u32 = ih4->src_address.as_u32;
- ih4->src_address.as_u32 = ih4->dst_address.as_u32;
- ih4->dst_address.as_u32 = src_ip40.as_u32;
-
- /* Chop the end of the pkt */
- b0->current_length += ip4_header_bytes (ih4) + tcp_hdr_len;
+ dst_ip40.as_u32 = ih4->dst_address.as_u32;
}
else
{
+ ih6 = vlib_buffer_get_current (b0);
ASSERT ((ih6->ip_version_traffic_class_and_flow_label & 0xF0) == 0x60);
clib_memcpy (&src_ip60, &ih6->src_address, sizeof (ip6_address_t));
- clib_memcpy (&ih6->src_address, &ih6->dst_address,
- sizeof (ip6_address_t));
- clib_memcpy (&ih6->dst_address, &src_ip60, sizeof (ip6_address_t));
-
- /* Chop the end of the pkt */
- b0->current_length += sizeof (ip6_header_t) + tcp_hdr_len;
+ clib_memcpy (&dst_ip60, &ih6->dst_address, sizeof (ip6_address_t));
}
- /* Try to determine what/why we're actually resetting and swap
- * src and dst ports */
+ src_port = th0->src_port;
+ dst_port = th0->dst_port;
+
+ /* Try to determine what/why we're actually resetting */
if (state == TCP_STATE_CLOSED)
{
if (!tcp_syn (th0))
tmp = clib_net_to_host_u32 (th0->seq_number);
/* Got a SYN for no listener. */
- th0->flags = TCP_FLAG_RST | TCP_FLAG_ACK;
- th0->ack_number = clib_host_to_net_u32 (tmp + 1);
- th0->seq_number = 0;
-
+ flags = TCP_FLAG_RST | TCP_FLAG_ACK;
+ ack = clib_host_to_net_u32 (tmp + 1);
+ seq = 0;
}
- else if (state >= TCP_STATE_SYN_SENT)
+ else
{
- th0->flags = TCP_FLAG_RST | TCP_FLAG_ACK;
- th0->seq_number = th0->ack_number;
- th0->ack_number = 0;
+ flags = TCP_FLAG_RST;
+ seq = th0->ack_number;
+ ack = 0;
}
- src_port0 = th0->src_port;
- th0->src_port = th0->dst_port;
- th0->dst_port = src_port0;
- th0->window = 0;
- th0->data_offset_and_reserved = (tcp_hdr_len >> 2) << 4;
- th0->urgent_pointer = 0;
+ tcp_reuse_buffer (vm, b0);
+ th0 = vlib_buffer_push_tcp_net_order (b0, dst_port, src_port, seq, ack,
+ sizeof (tcp_header_t), flags, 0);
- /* Compute checksum */
if (is_ip4)
{
+ ih4 = vlib_buffer_push_ip4 (vm, b0, &dst_ip40, &src_ip40,
+ IP_PROTOCOL_TCP);
th0->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ih4);
}
else
{
int bogus = ~0;
+ ih6 = vlib_buffer_push_ip6 (vm, b0, &dst_ip60, &src_ip60,
+ IP_PROTOCOL_TCP);
th0->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b0, ih6, &bogus);
ASSERT (!bogus);
}
tcp_make_fin (tc, b);
tcp_enqueue_to_output (vm, b, bi, tc->c_is_ip4);
tc->flags |= TCP_CONN_FINSNT;
+ tcp_retransmit_timer_force_update (tc);
TCP_EVT_DBG (TCP_EVT_FIN_SENT, tc);
}
u8 tcp_hdr_opts_len, opts_write_len, flags;
tcp_header_t *th;
- data_len = b->current_length;
+ data_len = b->current_length + b->total_length_not_including_first_buffer;
vnet_buffer (b)->tcp.flags = 0;
if (compute_opts)
tc->snd_opts_len = tcp_make_options (tc, &tc->snd_opts, tc->state);
- /* Write pre-computed options */
tcp_hdr_opts_len = tc->snd_opts_len + sizeof (tcp_header_t);
-
- /* Get rcv window to advertise */
advertise_wnd = tcp_window_to_advertise (tc, next_state);
flags = tcp_make_state_flags (next_state);
th = vlib_buffer_push_tcp (b, tc->c_lcl_port, tc->c_rmt_port, tc->snd_nxt,
tc->rcv_nxt, tcp_hdr_opts_len, flags,
advertise_wnd);
-
opts_write_len = tcp_options_write ((u8 *) (th + 1), &tc->snd_opts);
ASSERT (opts_write_len == tc->snd_opts_len);
-
- /* Tag the buffer with the connection index */
vnet_buffer (b)->tcp.connection_index = tc->c_c_index;
+ /*
+ * Update connection variables
+ */
+
tc->snd_nxt += data_len;
tc->rcv_las = tc->rcv_nxt;
/* TODO this is updated in output as well ... */
- if (tc->snd_nxt > tc->snd_una_max)
- tc->snd_una_max = tc->snd_nxt;
-
- if (tc->rtt_ts == 0)
+ if (seq_gt (tc->snd_nxt, tc->snd_una_max))
{
- tc->rtt_ts = tcp_time_now ();
- tc->rtt_seq = tc->snd_nxt;
+ tc->snd_una_max = tc->snd_nxt;
+ tcp_validate_txf_size (tc, tc->snd_una_max - tc->snd_una);
}
+
TCP_EVT_DBG (TCP_EVT_PKTIZE, tc);
}
*
* @return the number of bytes in the segment or 0 if there's nothing to
* retransmit
- * */
+ */
u32
tcp_prepare_retransmit_segment (tcp_connection_t * tc, vlib_buffer_t * b,
u32 offset, u32 max_bytes)
{
vlib_main_t *vm = vlib_get_main ();
- u32 n_bytes = 0;
+ int n_bytes = 0;
+ u32 start;
tcp_reuse_buffer (vm, b);
ASSERT (max_bytes != 0);
max_bytes = clib_min (tc->snd_mss, max_bytes);
+ start = tc->snd_una + offset;
/* Start is beyond snd_congestion */
- if (seq_geq (tc->snd_una + offset, tc->snd_congestion))
+ if (seq_geq (start, tc->snd_congestion))
goto done;
/* Don't overshoot snd_congestion */
- if (seq_gt (tc->snd_nxt + max_bytes, tc->snd_congestion))
+ if (seq_gt (start + max_bytes, tc->snd_congestion))
{
- max_bytes = tc->snd_congestion - tc->snd_nxt;
+ max_bytes = tc->snd_congestion - start;
if (max_bytes == 0)
goto done;
}
n_bytes = stream_session_peek_bytes (&tc->connection,
vlib_buffer_get_current (b), offset,
max_bytes);
- ASSERT (n_bytes != 0);
+ ASSERT (n_bytes > 0);
b->current_length = n_bytes;
tcp_push_hdr_i (tc, b, tc->state, 0);
- tc->rtx_bytes += n_bytes;
+
+ if (tcp_in_fastrecovery (tc))
+ tc->snd_rxt_bytes += n_bytes;
done:
TCP_EVT_DBG (TCP_EVT_CC_RTX, tc, offset, n_bytes);
static void
tcp_rtx_timeout_cc (tcp_connection_t * tc)
{
+ tc->prev_ssthresh = tc->ssthresh;
+ tc->prev_cwnd = tc->cwnd;
+
/* Cleanly recover cc (also clears up fast retransmit) */
if (tcp_in_fastrecovery (tc))
- {
- tcp_cc_recover (tc);
- }
- else
- {
- tc->ssthresh = clib_max (tcp_flight_size (tc) / 2, 2 * tc->snd_mss);
- }
+ tcp_cc_fastrecovery_exit (tc);
/* Start again from the beginning */
-
+ tc->ssthresh = clib_max (tcp_flight_size (tc) / 2, 2 * tc->snd_mss);
tc->cwnd = tcp_loss_wnd (tc);
tc->snd_congestion = tc->snd_una_max;
tcp_recovery_on (tc);
/* Make sure timer handle is set to invalid */
tc->timers[TCP_TIMER_RETRANSMIT] = TCP_TIMER_HANDLE_INVALID;
+ if (!tcp_in_recovery (tc) && tc->rto_boff > 0
+ && tc->state >= TCP_STATE_ESTABLISHED)
+ {
+ tc->rto_boff = 0;
+ tcp_update_rto (tc);
+ }
+
/* Increment RTO backoff (also equal to number of retries) */
tc->rto_boff += 1;
/* Go back to first un-acked byte */
tc->snd_nxt = tc->snd_una;
- /* Get buffer */
tcp_get_free_buffer_index (tm, &bi);
b = vlib_get_buffer (vm, bi);
if (tc->state >= TCP_STATE_ESTABLISHED)
{
+ /* Lost FIN, retransmit and return */
+ if (tc->flags & TCP_CONN_FINSNT)
+ {
+ tcp_send_fin (tc);
+ return;
+ }
+
/* First retransmit timeout */
if (tc->rto_boff == 1)
tcp_rtx_timeout_cc (tc);
TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 1);
- /* Send one segment. No fancy recovery for now! */
+ /* Send one segment */
n_bytes = tcp_prepare_retransmit_segment (tc, b, 0, tc->snd_mss);
+ /* TODO be less aggressive about this */
scoreboard_clear (&tc->sack_sb);
if (n_bytes == 0)
{
- clib_warning ("could not retransmit");
+ clib_warning ("could not retransmit anything");
+ clib_warning ("%U", format_tcp_connection, tc, 2);
+
+ /* Try again eventually */
+ tcp_retransmit_timer_set (tc);
+ ASSERT (0 || (tc->rto_boff > 1
+ && tc->snd_una == tc->snd_congestion));
return;
}
+
+ /* For first retransmit, record timestamp (Eifel detection RFC3522) */
+ if (tc->rto_boff == 1)
+ tc->snd_rxt_ts = tcp_time_now ();
}
- else
+ /* Retransmit for SYN/SYNACK */
+ else if (tc->state == TCP_STATE_SYN_RCVD || tc->state == TCP_STATE_SYN_SENT)
{
- /* Retransmit for SYN/SYNACK */
- ASSERT (tc->state == TCP_STATE_SYN_RCVD
- || tc->state == TCP_STATE_SYN_SENT);
-
/* Try without increasing RTO a number of times. If this fails,
* start growing RTO exponentially */
if (tc->rto_boff > TCP_RTO_SYN_RETRIES)
/* Account for the SYN */
tc->snd_nxt += 1;
}
+ else
+ {
+ ASSERT (tc->state == TCP_STATE_CLOSED);
+ clib_warning ("connection closed ...");
+ return;
+ }
if (!is_syn)
{
u32 thread_index = vlib_get_thread_index ();
tcp_connection_t *tc;
vlib_buffer_t *b;
- u32 bi, n_bytes;
+ u32 bi, old_snd_nxt;
+ int n_bytes = 0;
- tc = tcp_connection_get (index, thread_index);
+ tc = tcp_connection_get_if_valid (index, thread_index);
+
+ if (!tc)
+ return;
/* Make sure timer handle is set to invalid */
tc->timers[TCP_TIMER_PERSIST] = TCP_TIMER_HANDLE_INVALID;
/* Problem already solved or worse */
- if (tc->snd_wnd > tc->snd_mss || tcp_in_recovery (tc))
+ if (tc->state == TCP_STATE_CLOSED
+ || tc->snd_wnd > tc->snd_mss || tcp_in_recovery (tc))
return;
/* Increment RTO backoff */
/* Try to force the first unsent segment */
tcp_get_free_buffer_index (tm, &bi);
b = vlib_get_buffer (vm, bi);
+
+ tcp_validate_txf_size (tc, tc->snd_una_max - tc->snd_una);
tc->snd_opts_len = tcp_make_options (tc, &tc->snd_opts, tc->state);
n_bytes = stream_session_peek_bytes (&tc->connection,
vlib_buffer_get_current (b),
tc->snd_una_max - tc->snd_una,
tc->snd_mss);
/* Nothing to send */
- if (n_bytes == 0)
+ if (n_bytes <= 0)
{
+ clib_warning ("persist found nothing to send");
tcp_return_buffer (tm);
return;
}
b->current_length = n_bytes;
+ ASSERT (tc->snd_nxt == tc->snd_una_max || tc->rto_boff > 1
+ || tcp_timer_is_active (tc, TCP_TIMER_RETRANSMIT));
+
+ /* Allow updating of snd_una_max but don't update snd_nxt */
+ old_snd_nxt = tc->snd_nxt;
tcp_push_hdr_i (tc, b, tc->state, 0);
+ tc->snd_nxt = old_snd_nxt;
tcp_enqueue_to_output (vm, b, bi, tc->c_is_ip4);
/* Re-enable persist timer */
tcp_main_t *tm = vnet_get_tcp_main ();
vlib_main_t *vm = vlib_get_main ();
vlib_buffer_t *b;
- u32 bi, n_bytes;
+ u32 bi, n_bytes, old_snd_nxt;
+ old_snd_nxt = tc->snd_nxt;
tc->snd_nxt = tc->snd_una;
/* Get buffer */
n_bytes = tcp_prepare_retransmit_segment (tc, b, 0, tc->snd_mss);
if (n_bytes == 0)
- goto done;
+ {
+ tcp_return_buffer (tm);
+ goto done;
+ }
tcp_enqueue_to_output (vm, b, bi, tc->c_is_ip4);
done:
- tc->snd_nxt = tc->snd_una_max;
+ tc->snd_nxt = old_snd_nxt;
}
-sack_scoreboard_hole_t *
-scoreboard_first_rtx_hole (sack_scoreboard_t * sb)
+/**
+ * Do fast retransmit with SACKs
+ */
+void
+tcp_fast_retransmit_sack (tcp_connection_t * tc)
{
- sack_scoreboard_hole_t *hole = 0;
-
-// hole = scoreboard_first_hole (&tc->sack_sb);
-// if (hole)
-// {
-//
-// offset = hole->start - tc->snd_una;
-// hole_size = hole->end - hole->start;
-//
-// ASSERT(hole_size);
-//
-// if (hole_size < max_bytes)
-// max_bytes = hole_size;
-// }
- return hole;
+ tcp_main_t *tm = vnet_get_tcp_main ();
+ vlib_main_t *vm = vlib_get_main ();
+ u32 n_written = 0, offset = 0, max_bytes;
+ vlib_buffer_t *b;
+ sack_scoreboard_hole_t *hole;
+ sack_scoreboard_t *sb;
+ u32 bi, old_snd_nxt;
+ int snd_space;
+ u8 snd_limited = 0, can_rescue = 0;
+
+ ASSERT (tcp_in_fastrecovery (tc));
+ TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 0);
+
+ old_snd_nxt = tc->snd_nxt;
+ sb = &tc->sack_sb;
+ snd_space = tcp_available_snd_space (tc);
+
+ hole = scoreboard_get_hole (sb, sb->cur_rxt_hole);
+ while (hole && snd_space > 0)
+ {
+ tcp_get_free_buffer_index (tm, &bi);
+ b = vlib_get_buffer (vm, bi);
+
+ hole = scoreboard_next_rxt_hole (sb, hole,
+ tcp_fastrecovery_sent_1_smss (tc),
+ &can_rescue, &snd_limited);
+ if (!hole)
+ {
+ if (!can_rescue || !(seq_lt (sb->rescue_rxt, tc->snd_una)
+ || seq_gt (sb->rescue_rxt,
+ tc->snd_congestion)))
+ break;
+
+ /* If rescue rxt undefined or less than snd_una then one segment of
+ * up to SMSS octets that MUST include the highest outstanding
+ * unSACKed sequence number SHOULD be returned, and RescueRxt set to
+ * RecoveryPoint. HighRxt MUST NOT be updated.
+ */
+ max_bytes = clib_min (tc->snd_mss, snd_space);
+ offset = tc->snd_congestion - tc->snd_una - max_bytes;
+ sb->rescue_rxt = tc->snd_congestion;
+ tc->snd_nxt = tc->snd_una + offset;
+ tcp_prepare_retransmit_segment (tc, b, offset, max_bytes);
+ tcp_enqueue_to_output (vm, b, bi, tc->c_is_ip4);
+ break;
+ }
+
+ max_bytes = snd_limited ? tc->snd_mss : hole->end - sb->high_rxt;
+ offset = sb->high_rxt - tc->snd_una;
+ tc->snd_nxt = tc->snd_una + offset;
+ n_written = tcp_prepare_retransmit_segment (tc, b, offset, max_bytes);
+
+ /* Nothing left to retransmit */
+ if (n_written == 0)
+ {
+ tcp_return_buffer (tm);
+ break;
+ }
+
+ sb->high_rxt += n_written;
+ tcp_enqueue_to_output (vm, b, bi, tc->c_is_ip4);
+ snd_space -= n_written;
+ }
+
+ /* If window allows, send 1 SMSS of new data */
+ tc->snd_nxt = old_snd_nxt;
}
/**
- * Do fast retransmit.
+ * Fast retransmit without SACK info
*/
void
-tcp_fast_retransmit (tcp_connection_t * tc)
+tcp_fast_retransmit_no_sack (tcp_connection_t * tc)
{
tcp_main_t *tm = vnet_get_tcp_main ();
vlib_main_t *vm = vlib_get_main ();
- u32 bi;
+ u32 n_written = 0, offset = 0, bi, old_snd_nxt;
int snd_space;
- u32 n_written = 0, offset = 0;
vlib_buffer_t *b;
- u8 use_sacks = 0;
ASSERT (tcp_in_fastrecovery (tc));
+ TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 0);
/* Start resending from first un-acked segment */
+ old_snd_nxt = tc->snd_nxt;
tc->snd_nxt = tc->snd_una;
-
snd_space = tcp_available_snd_space (tc);
- TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 0);
-
- /* If we have SACKs use them */
- if (tcp_opts_sack_permitted (&tc->opt)
- && scoreboard_first_hole (&tc->sack_sb))
- use_sacks = 0;
while (snd_space > 0)
{
tcp_get_free_buffer_index (tm, &bi);
b = vlib_get_buffer (vm, bi);
- if (use_sacks)
- {
- scoreboard_first_rtx_hole (&tc->sack_sb);
- }
- else
- {
- offset += n_written;
- }
-
+ offset += n_written;
n_written = tcp_prepare_retransmit_segment (tc, b, offset, snd_space);
/* Nothing left to retransmit */
snd_space -= n_written;
}
- /* If window allows, send 1 SMSS of new data */
- if (seq_lt (tc->snd_nxt, tc->snd_congestion))
- tc->snd_nxt = tc->snd_congestion;
+ /* Restore snd_nxt. If window allows, send 1 SMSS of new data */
+ tc->snd_nxt = old_snd_nxt;
+}
+
+/**
+ * Do fast retransmit
+ */
+void
+tcp_fast_retransmit (tcp_connection_t * tc)
+{
+ if (tcp_opts_sack_permitted (&tc->rcv_opts)
+ && scoreboard_first_hole (&tc->sack_sb))
+ tcp_fast_retransmit_sack (tc);
+ else
+ tcp_fast_retransmit_no_sack (tc);
}
always_inline u32
tc = (tcp_connection_t *) tconn;
tcp_push_hdr_i (tc, b, TCP_STATE_ESTABLISHED, 0);
+
+ if (tc->rtt_ts == 0)
+ {
+ tc->rtt_ts = tcp_time_now ();
+ tc->rtt_seq = tc->snd_nxt;
+ }
return 0;
}