return wnd_scale;
}
+/**
+ * TCP's IW as recommended by RFC6928
+ */
+always_inline u32
+tcp_initial_wnd_unscaled (tcp_connection_t * tc)
+{
+ return TCP_IW_N_SEGMENTS * dummy_mtu;
+}
+
/**
* Compute initial window and scale factor. As per RFC1323, window field in
* SYN and SYN-ACK segments is never scaled.
u32
tcp_initial_window_to_advertise (tcp_connection_t * tc)
{
- u32 available_space;
+ u32 max_fifo;
/* Initial wnd for SYN. Fifos are not allocated yet.
- * Use some predefined value */
- if (tc->state != TCP_STATE_SYN_RCVD)
- {
- return TCP_DEFAULT_RX_FIFO_SIZE;
- }
+ * Use some predefined value. For SYN-ACK we still want the
+ * scale to be computed in the same way */
+ max_fifo = TCP_MAX_RX_FIFO_SIZE;
- available_space = stream_session_max_enqueue (&tc->connection);
- tc->rcv_wscale = tcp_window_compute_scale (available_space);
- tc->rcv_wnd = clib_min (available_space, TCP_WND_MAX << tc->rcv_wscale);
+ tc->rcv_wscale = tcp_window_compute_scale (max_fifo);
+ tc->rcv_wnd = tcp_initial_wnd_unscaled (tc);
return clib_min (tc->rcv_wnd, TCP_WND_MAX);
}
u32
tcp_window_to_advertise (tcp_connection_t * tc, tcp_state_t state)
{
- u32 available_space, wnd, scaled_space;
+ u32 available_space, max_fifo, observed_wnd;
- if (state != TCP_STATE_ESTABLISHED)
+ if (state < TCP_STATE_ESTABLISHED)
return tcp_initial_window_to_advertise (tc);
+ /*
+ * Figure out how much space we have available
+ */
available_space = stream_session_max_enqueue (&tc->connection);
- scaled_space = available_space >> tc->rcv_wscale;
+ max_fifo = stream_session_fifo_size (&tc->connection);
- /* Need to update scale */
- if (PREDICT_FALSE ((scaled_space == 0 && available_space != 0))
- || (scaled_space >= TCP_WND_MAX))
- tc->rcv_wscale = tcp_window_compute_scale (available_space);
+ ASSERT (tc->opt.mss < max_fifo);
- wnd = clib_min (available_space, TCP_WND_MAX << tc->rcv_wscale);
- tc->rcv_wnd = wnd;
+ if (available_space < tc->opt.mss && available_space < max_fifo / 8)
+ available_space = 0;
+
+ /*
+ * Use the above and what we know about what we've previously advertised
+ * to compute the new window
+ */
+ observed_wnd = tc->rcv_wnd - (tc->rcv_nxt - tc->rcv_las);
+
+ /* Bad. Thou shalt not shrink */
+ if (available_space < observed_wnd)
+ {
+ if (available_space == 0)
+ clib_warning ("Didn't shrink rcv window despite not having space");
+ }
+
+ tc->rcv_wnd = clib_min (available_space, TCP_WND_MAX << tc->rcv_wscale);
+
+ if (tc->rcv_wnd == 0)
+ {
+ tc->flags |= TCP_CONN_SENT_RCV_WND0;
+ }
- return wnd >> tc->rcv_wscale;
+ return tc->rcv_wnd >> tc->rcv_wscale;
}
/**
}
always_inline int
-tcp_make_syn_options (tcp_options_t * opts, u32 initial_wnd)
+tcp_make_syn_options (tcp_options_t * opts, u8 wnd_scale)
{
u8 len = 0;
len += TCP_OPTION_LEN_MSS;
opts->flags |= TCP_OPTS_FLAG_WSCALE;
- opts->wscale = tcp_window_compute_scale (initial_wnd);
+ opts->wscale = wnd_scale;
len += TCP_OPTION_LEN_WINDOW_SCALE;
opts->flags |= TCP_OPTS_FLAG_TSTAMP;
case TCP_STATE_SYN_RCVD:
return tcp_make_synack_options (tc, opts);
case TCP_STATE_SYN_SENT:
- return tcp_make_syn_options (opts,
- tcp_initial_window_to_advertise (tc));
+ return tcp_make_syn_options (opts, tc->rcv_wscale);
default:
clib_warning ("Not handled!");
return 0;
/* Leave enough space for headers */
vlib_buffer_make_headroom (b, MAX_HDRS_LEN);
+ vnet_buffer (b)->tcp.flags = 0;
}
/**
* Convert buffer to FIN-ACK
*/
void
-tcp_make_finack (tcp_connection_t * tc, vlib_buffer_t * b)
+tcp_make_fin (tcp_connection_t * tc, vlib_buffer_t * b)
{
tcp_main_t *tm = vnet_get_tcp_main ();
vlib_main_t *vm = tm->vlib_main;
+ u8 flags = 0;
tcp_reuse_buffer (vm, b);
- tcp_make_ack_i (tc, b, TCP_STATE_ESTABLISHED, TCP_FLAG_ACK | TCP_FLAG_FIN);
+
+ if (tc->rcv_las == tc->rcv_nxt)
+ flags = TCP_FLAG_FIN;
+ else
+ flags = TCP_FLAG_FIN | TCP_FLAG_ACK;
+
+ tcp_make_ack_i (tc, b, TCP_STATE_ESTABLISHED, flags);
/* Reset flags, make sure ack is sent */
- tc->flags = TCP_CONN_SNDACK;
vnet_buffer (b)->tcp.flags &= ~TCP_BUF_FLAG_DUPACK;
tc->snd_nxt += 1;
vnet_buffer (b)->tcp.flags = TCP_BUF_FLAG_ACK;
/* Init retransmit timer */
- tcp_retransmit_timer_set (tm, tc);
+ tcp_retransmit_timer_set (tc);
}
always_inline void
/* Make and write options */
memset (&snd_opts, 0, sizeof (snd_opts));
- tcp_opts_len = tcp_make_syn_options (&snd_opts, initial_wnd);
+ tcp_opts_len = tcp_make_syn_options (&snd_opts, tc->rcv_wscale);
tcp_hdr_opts_len = tcp_opts_len + sizeof (tcp_header_t);
th = vlib_buffer_push_tcp (b, tc->c_lcl_port, tc->c_rmt_port, tc->iss,
/* Leave enough space for headers */
vlib_buffer_make_headroom (b, MAX_HDRS_LEN);
- tcp_make_finack (tc, b);
-
+ tcp_make_fin (tc, b);
tcp_enqueue_to_output (vm, b, bi, tc->c_is_ip4);
+ tc->flags |= TCP_CONN_FINSNT;
}
always_inline u8
tcp_reuse_buffer (vm, b);
- ASSERT (tc->state == TCP_STATE_ESTABLISHED);
+ ASSERT (tc->state >= TCP_STATE_ESTABLISHED);
ASSERT (max_bytes != 0);
if (tcp_opts_sack_permitted (&tc->opt))
max_bytes);
ASSERT (n_bytes != 0);
- tc->snd_nxt += n_bytes;
tcp_push_hdr_i (tc, b, tc->state);
return n_bytes;
tcp_get_free_buffer_index (tm, &bi);
b = vlib_get_buffer (vm, bi);
- if (tc->state == TCP_STATE_ESTABLISHED)
+ if (tc->state >= TCP_STATE_ESTABLISHED)
{
tcp_fastrecovery_off (tc);
/* Figure out what and how many bytes we can send */
snd_space = tcp_available_snd_space (tc);
max_bytes = clib_min (tc->snd_mss, snd_space);
+
+ if (max_bytes == 0)
+ {
+ clib_warning ("no wnd to retransmit");
+ return;
+ }
tcp_prepare_retransmit_segment (tc, b, max_bytes);
tc->rtx_bytes += max_bytes;
tc->rto = clib_min (tc->rto << 1, TCP_RTO_MAX);
vlib_buffer_make_headroom (b, MAX_HDRS_LEN);
+
tcp_push_hdr_i (tc, b, tc->state);
+
+ /* Account for the SYN */
+ tc->snd_nxt += 1;
}
if (!is_syn)
tcp_enqueue_to_output (vm, b, bi, tc->c_is_ip4);
/* Re-enable retransmit timer */
- tcp_retransmit_timer_set (tm, tc);
+ tcp_retransmit_timer_set (tc);
}
else
{
vlib_node_runtime_t * node,
vlib_frame_t * from_frame, int is_ip4)
{
- tcp_main_t *tm = vnet_get_tcp_main ();
u32 n_left_from, next_index, *from, *to_next;
u32 my_thread_index = vm->cpu_index;
b0 = vlib_get_buffer (vm, bi0);
tc0 = tcp_connection_get (vnet_buffer (b0)->tcp.connection_index,
my_thread_index);
+ if (PREDICT_FALSE (tc0 == 0 || tc0->state == TCP_STATE_CLOSED))
+ {
+ error0 = TCP_ERROR_INVALID_CONNECTION;
+ next0 = TCP_OUTPUT_NEXT_DROP;
+ goto done;
+ }
+
th0 = vlib_buffer_get_current (b0);
if (is_ip4)
if (PREDICT_FALSE
(vnet_buffer (b0)->tcp.flags & TCP_BUF_FLAG_DUPACK))
{
+ ASSERT (tc0->snt_dupacks > 0);
tc0->snt_dupacks--;
- ASSERT (tc0->snt_dupacks >= 0);
if (!tcp_session_has_ooo_data (tc0))
{
error0 = TCP_ERROR_FILTERED_DUPACKS;
tc0->rtt_ts = tcp_time_now ();
tc0->rtt_seq = tc0->snd_nxt;
}
+
+ if (1)
+ {
+ ELOG_TYPE_DECLARE (e) =
+ {
+ .format =
+ "output: snd_una %u snd_una_max %u",.format_args =
+ "i4i4",};
+ struct
+ {
+ u32 data[2];
+ } *ed;
+ ed = ELOG_DATA (&vm->elog_main, e);
+ ed->data[0] = tc0->snd_una - tc0->iss;
+ ed->data[1] = tc0->snd_una_max - tc0->iss;
+ }
}
/* Set the retransmit timer if not set already and not
if (!tcp_timer_is_active (tc0, TCP_TIMER_RETRANSMIT)
&& tc0->snd_nxt != tc0->snd_una)
{
- tcp_retransmit_timer_set (tm, tc0);
+ tcp_retransmit_timer_set (tc0);
tc0->rto_boff = 0;
}