if (tc->flags & TCP_CONN_RATE_SAMPLE)
tcp_bt_init (tc);
+
+ tc->start_ts = tcp_time_now_us (tc->c_thread_index);
}
static int
return s;
}
+static u8 *
+format_tcp_stats (u8 * s, va_list * args)
+{
+ tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
+ u32 indent = format_get_indent (s);
+ s = format (s, "in segs %lu dsegs %lu bytes %lu dupacks %u\n",
+ tc->segs_in, tc->data_segs_in, tc->bytes_in, tc->dupacks_in);
+ s = format (s, "%Uout segs %lu dsegs %lu bytes %lu dupacks %u\n",
+ format_white_space, indent, tc->segs_out,
+ tc->data_segs_out, tc->bytes_out, tc->dupacks_out);
+ s = format (s, "%Ufr %u tr %u rxt segs %lu bytes %lu duration %.3f\n",
+ format_white_space, indent, tc->fr_occurences,
+ tc->tr_occurences, tc->segs_retrans, tc->bytes_retrans,
+ tcp_time_now_us (tc->c_thread_index) - tc->start_ts);
+ s = format (s, "%Uerr wnd data below %u above %u ack below %u above %u",
+ format_white_space, indent, tc->errors.below_data_wnd,
+ tc->errors.above_data_wnd, tc->errors.below_ack_wnd,
+ tc->errors.above_ack_wnd);
+ return s;
+}
+
static u8 *
format_tcp_vars (u8 * s, va_list * args)
{
{
s = format (s, " sboard: %U\n", format_tcp_scoreboard, &tc->sack_sb,
tc);
+ s = format (s, " stats: %U\n", format_tcp_stats, tc);
}
if (vec_len (tc->snd_sacks))
s = format (s, " sacks tx: %U\n", format_tcp_sacks, tc);
TCP_CC_EVT_START_TX,
} tcp_cc_event_t;
+/*
+ * As per RFC4898 tcpEStatsStackSoftErrors
+ */
+typedef struct tcp_errors_
+{
+ u32 below_data_wnd; /**< All data in seg is below snd_una */
+ u32 above_data_wnd; /**< Some data in segment is above snd_wnd */
+ u32 below_ack_wnd; /**< Acks for data below snd_una */
+ u32 above_ack_wnd; /**< Acks for data not sent */
+} tcp_errors_t;
+
typedef struct _tcp_connection
{
CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
u16 flags; /**< Connection flags (see tcp_conn_flags_e) */
u32 timers[TCP_N_TIMERS]; /**< Timer handles into timer wheel */
- /* TODO RFC4898 */
+ u64 segs_in; /** RFC4022/4898 tcpHCInSegs/tcpEStatsPerfSegsIn */
+ u64 bytes_in; /** RFC4898 tcpEStatsPerfHCDataOctetsIn */
+ u64 segs_out; /** RFC4898 tcpEStatsPerfSegsOut */
+ u64 bytes_out; /** RFC4898 tcpEStatsPerfHCDataOctetsOut */
/** Send sequence variables RFC793 */
u32 snd_una; /**< oldest unacknowledged sequence number */
u32 snd_nxt; /**< next seq number to be sent */
u16 snd_mss; /**< Effective send max seg (data) size */
+ u64 data_segs_in; /** RFC4898 tcpEStatsPerfDataSegsIn */
+ u64 data_segs_out; /** RFC4898 tcpEStatsPerfDataSegsOut */
+
/** Receive sequence variables RFC793 */
u32 rcv_nxt; /**< next sequence number expected */
u32 rcv_wnd; /**< receive window we expect */
sack_block_t *snd_sacks_fl; /**< Vector for building new list */
sack_scoreboard_t sack_sb; /**< SACK "scoreboard" that tracks holes */
- u16 rcv_dupacks; /**< Number of DUPACKs received */
+ u16 rcv_dupacks; /**< Number of recent DUPACKs received */
+ u32 dupacks_in; /**< RFC4898 tcpEStatsStackDupAcksIn*/
u8 pending_dupacks; /**< Number of DUPACKs to be sent */
+ u32 dupacks_out; /**< RFC4898 tcpEStatsPathDupAcksOut */
/* Congestion control */
u32 cwnd; /**< Congestion window */
tcp_cc_algorithm_t *cc_algo; /**< Congestion control algorithm */
u8 cc_data[TCP_CC_DATA_SZ]; /**< Congestion control algo private data */
+ u32 fr_occurences; /**< fast-retransmit occurrences RFC4898
+ tcpEStatsStackFastRetran */
+ u32 tr_occurences; /**< timer-retransmit occurrences */
+ u64 bytes_retrans; /**< RFC4898 tcpEStatsPerfOctetsRetrans */
+ u64 segs_retrans; /**< RFC4898 tcpEStatsPerfSegsRetrans*/
+
/* RTT and RTO */
u32 rto; /**< Retransmission timeout */
u32 rto_boff; /**< Index for RTO backoff */
f64 delivered_time; /**< Time last bytes were acked */
tcp_byte_tracker_t *bt; /**< Tx byte tracker */
+ tcp_errors_t errors; /**< Soft connection errors */
+
+ f64 start_ts; /**< Timestamp when connection initialized */
u32 last_fib_check; /**< Last time we checked fib route for peer */
u16 mss; /**< Our max seg size that includes options */
- u32 timestamp_delta;
+ u32 timestamp_delta; /**< Offset for timestamp */
} tcp_connection_t;
/* *INDENT-OFF* */
*error0 = TCP_ERROR_RCV_WND;
+ tc0->errors.below_data_wnd += seq_lt (vnet_buffer (b0)->tcp.seq_end,
+ tc0->rcv_las);
+
/* If not RST, send dup ack */
if (!tcp_rst (th0))
{
tc->prev_ssthresh = tc->ssthresh;
tc->prev_cwnd = tc->cwnd;
tc->cc_algo->congestion (tc);
+ tc->fr_occurences += 1;
TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 4);
}
#endif /* CLIB_MARCH_VARIANT */
goto process_ack;
}
+ tc->errors.above_ack_wnd += 1;
*error = TCP_ERROR_ACK_FUTURE;
TCP_EVT_DBG (TCP_EVT_ACK_RCV_ERR, tc, 0,
vnet_buffer (b)->tcp.ack_number);
/* If old ACK, probably it's an old dupack */
if (PREDICT_FALSE (seq_lt (vnet_buffer (b)->tcp.ack_number, tc->snd_una)))
{
+ tc->errors.below_ack_wnd += 1;
*error = TCP_ERROR_ACK_OLD;
TCP_EVT_DBG (TCP_EVT_ACK_RCV_ERR, tc, 1,
vnet_buffer (b)->tcp.ack_number);
/*
* Looks okay, process feedback
*/
+
if (tcp_opts_sack_permitted (&tc->rcv_opts))
tcp_rcv_sacks (tc, vnet_buffer (b)->tcp.ack_number);
if (tcp_ack_is_cc_event (tc, b, prev_snd_wnd, prev_snd_una, &is_dack))
{
tcp_cc_handle_event (tc, &rs, is_dack);
+ tc->dupacks_in += is_dack;
if (!tcp_in_cong_recovery (tc))
{
*error = TCP_ERROR_ACK_OK;
ASSERT (data_len);
written = session_enqueue_stream_connection (&tc->connection, b, 0,
1 /* queue event */ , 1);
+ tc->bytes_in += written;
TCP_EVT_DBG (TCP_EVT_INPUT, tc, 0, data_len, written);
}
TCP_EVT_DBG (TCP_EVT_INPUT, tc, 1, data_len, data_len);
+ tc->bytes_in += data_len;
/* Update SACK list if in use */
if (tcp_opts_sack_permitted (&tc->rcv_opts))
vlib_buffer_advance (b, vnet_buffer (b)->tcp.data_offset);
n_data_bytes = vnet_buffer (b)->tcp.data_len;
ASSERT (n_data_bytes);
+ tc->data_segs_in += 1;
/* Handle out-of-order data */
if (PREDICT_FALSE (vnet_buffer (b)->tcp.seq_number != tc->rcv_nxt))
error = tcp_session_enqueue_ooo (tc, b, n_data_bytes);
tcp_program_dupack (tc);
TCP_EVT_DBG (TCP_EVT_DUPACK_SENT, tc, vnet_buffer (b)->tcp);
+ tc->errors.above_data_wnd += seq_gt (vnet_buffer (b)->tcp.seq_end,
+ tc->rcv_las + tc->rcv_wnd);
goto done;
}
flags = tcp->flags & filter_flags;
*next = tm->dispatch_table[tc->state][flags].next;
*error = tm->dispatch_table[tc->state][flags].error;
+ tc->segs_in += 1;
if (PREDICT_FALSE (*error == TCP_ERROR_DISPATCH
|| *next == TCP_INPUT_NEXT_RESET))
tc->snd_nxt += data_len;
tc->rcv_las = tc->rcv_nxt;
+ tc->bytes_out += data_len;
+ tc->data_segs_out += 1;
+
TCP_EVT_DBG (TCP_EVT_PKTIZE, tc);
}
/* Start is beyond snd_congestion */
start = tc->snd_una + offset;
if (seq_geq (start, tc->snd_congestion))
- goto done;
+ return 0;
/* Don't overshoot snd_congestion */
if (seq_gt (start + max_deq_bytes, tc->snd_congestion))
{
max_deq_bytes = tc->snd_congestion - start;
if (max_deq_bytes == 0)
- goto done;
+ return 0;
}
n_bytes = tcp_prepare_segment (wrk, tc, offset, max_deq_bytes, b);
tcp_bt_track_rxt (tc, start, start + n_bytes);
}
-done:
+ tc->bytes_retrans += n_bytes;
+ tc->segs_retrans += 1;
TCP_EVT_DBG (TCP_EVT_CC_RTX, tc, offset, n_bytes);
return n_bytes;
}
tc->rcv_dupacks = 0;
tc->rtt_ts = 0;
tc->cwnd_acc_bytes = 0;
+ tc->tr_occurences += 1;
tcp_connection_tx_pacer_reset (tc, tc->cwnd, 2 * tc->snd_mss);
tcp_recovery_on (tc);
}
{
tc->pending_dupacks = 0;
tc->snd_sack_pos = 0;
+ tc->dupacks_out += n_acks;
return n_acks;
}
else
{
TCP_DBG ("constrained by burst size");
tc->pending_dupacks = n_acks - max_burst_size;
+ tc->dupacks_out += max_burst_size;
tcp_program_dupack (tc);
return max_burst_size;
}
if (!TCP_ALWAYS_ACK)
tcp_timer_reset (tc0, TCP_TIMER_DELACK);
+
+ tc0->segs_out += 1;
}
always_inline uword