&& seq_leq (seq, tc->rcv_nxt + tc->rcv_wnd));
}
-void
+/**
+ * Parse TCP header options.
+ *
+ * @param th TCP header
+ * @param to TCP options data structure to be populated
+ * @return -1 if parsing failed
+ */
+int
tcp_options_parse (tcp_header_t * th, tcp_options_t * to)
{
const u8 *data;
if (kind == TCP_OPTION_EOL)
break;
else if (kind == TCP_OPTION_NOOP)
- opt_len = 1;
+ {
+ opt_len = 1;
+ continue;
+ }
else
{
/* broken options */
if (opts_len < 2)
- break;
+ return -1;
opt_len = data[1];
/* weird option length */
if (opt_len < 2 || opt_len > opts_len)
- break;
+ return -1;
}
/* Parse options */
continue;
}
}
+ return 0;
}
/**
if (PREDICT_FALSE (!tcp_ack (th0) && !tcp_rst (th0) && !tcp_syn (th0)))
return -1;
- tcp_options_parse (th0, &tc0->opt);
+ if (PREDICT_FALSE (tcp_options_parse (th0, &tc0->opt)))
+ {
+ return -1;
+ }
if (tcp_segment_check_paws (tc0))
{
tcp_segment_rcv (tcp_main_t * tm, tcp_connection_t * tc, vlib_buffer_t * b,
u16 n_data_bytes, u32 * next0)
{
- u32 error = 0;
+ u32 error = 0, n_bytes_to_drop;
/* Handle out-of-order data */
if (PREDICT_FALSE (vnet_buffer (b)->tcp.seq_number != tc->rcv_nxt))
{
/* Old sequence numbers allowed through because they overlapped
* the rx window */
-
if (seq_lt (vnet_buffer (b)->tcp.seq_number, tc->rcv_nxt))
{
error = TCP_ERROR_SEGMENT_OLD;
*next0 = TCP_NEXT_DROP;
- goto done;
+
+ /* Chop off the bytes in the past */
+ n_bytes_to_drop = tc->rcv_nxt - vnet_buffer (b)->tcp.seq_number;
+ n_data_bytes -= n_bytes_to_drop;
+ vlib_buffer_advance (b, n_bytes_to_drop);
+
+ goto in_order;
}
error = tcp_session_enqueue_ooo (tc, b, n_data_bytes);
goto done;
}
+in_order:
+
/* In order data, enqueue. Fifo figures out by itself if any out-of-order
* segments can be enqueued after fifo tail offset changes. */
error = tcp_session_enqueue_data (tc, b, n_data_bytes);
new_tc0->irs = seq0;
/* Parse options */
- tcp_options_parse (tcp0, &new_tc0->opt);
+ if (tcp_options_parse (tcp0, &new_tc0->opt))
+ goto drop;
if (tcp_opts_tstamp (&new_tc0->opt))
{
case TCP_STATE_FIN_WAIT_2:
/* Got FIN, send ACK! */
tc0->state = TCP_STATE_TIME_WAIT;
+ tcp_connection_timers_reset (tc0);
tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME);
tcp_make_ack (tc0, b0);
next0 = tcp_next_output (is_ip4);
goto drop;
}
- tcp_options_parse (th0, &child0->opt);
+ if (tcp_options_parse (th0, &child0->opt))
+ {
+ goto drop;
+ }
child0->irs = vnet_buffer (b0)->tcp.seq_number;
child0->rcv_nxt = vnet_buffer (b0)->tcp.seq_number + 1;
_(FIN_WAIT_2, TCP_FLAG_FIN | 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_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
_(CLOSED, TCP_FLAG_ACK, TCP_INPUT_NEXT_RESET, TCP_ERROR_CONNECTION_CLOSED);
_(CLOSED, TCP_FLAG_RST, TCP_INPUT_NEXT_DROP, TCP_ERROR_CONNECTION_CLOSED);
#undef _
/* XXX check if MTU has been updated */
tc->snd_mss = clib_min (tc->mss, tc->opt.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);
- 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->opt.mss = default_min_mss;
}
/* We should have enough space for 40 bytes of options */
vlib_buffer_t *b;
u32 bi, n_bytes;
- 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 */