+int
+tcp_session_custom_tx (void *conn, transport_send_params_t * sp)
+{
+ tcp_connection_t *tc = (tcp_connection_t *) conn;
+ u32 n_segs = 0;
+
+ if (tcp_in_cong_recovery (tc) && (tc->flags & TCP_CONN_RXT_PENDING))
+ {
+ tc->flags &= ~TCP_CONN_RXT_PENDING;
+ n_segs = tcp_do_retransmit (tc, sp->max_burst_size);
+ }
+
+ if (!(tc->flags & TCP_CONN_SNDACK))
+ return n_segs;
+
+ tc->flags &= ~TCP_CONN_SNDACK;
+
+ /* We have retransmitted packets and no dupack */
+ if (n_segs && !tc->pending_dupacks)
+ return n_segs;
+
+ if (sp->max_burst_size <= n_segs)
+ {
+ tcp_program_ack (tc);
+ return n_segs;
+ }
+
+ n_segs += tcp_send_acks (tc, sp->max_burst_size - n_segs);
+
+ return n_segs;
+}
+#endif /* CLIB_MARCH_VARIANT */
+
+static void
+tcp_output_handle_link_local (tcp_connection_t * tc0, vlib_buffer_t * b0,
+ u16 * next0, u32 * error0)
+{
+ ip_adjacency_t *adj;
+ adj_index_t ai;
+
+ /* Not thread safe but as long as the connection exists the adj should
+ * not be removed */
+ ai = adj_nbr_find (FIB_PROTOCOL_IP6, VNET_LINK_IP6, &tc0->c_rmt_ip,
+ tc0->sw_if_index);
+ if (ai == ADJ_INDEX_INVALID)
+ {
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0;
+ *next0 = TCP_OUTPUT_NEXT_DROP;
+ *error0 = TCP_ERROR_LINK_LOCAL_RW;
+ return;
+ }
+
+ adj = adj_get (ai);
+ if (PREDICT_TRUE (adj->lookup_next_index == IP_LOOKUP_NEXT_REWRITE))
+ *next0 = TCP_OUTPUT_NEXT_IP_REWRITE;
+ else if (adj->lookup_next_index == IP_LOOKUP_NEXT_ARP)
+ *next0 = TCP_OUTPUT_NEXT_IP_ARP;
+ else
+ {
+ *next0 = TCP_OUTPUT_NEXT_DROP;
+ *error0 = TCP_ERROR_LINK_LOCAL_RW;
+ }
+ vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ai;
+}
+
+static void
+tcp46_output_trace_frame (vlib_main_t * vm, vlib_node_runtime_t * node,
+ u32 * to_next, u32 n_bufs)
+{
+ tcp_connection_t *tc;
+ tcp_tx_trace_t *t;
+ vlib_buffer_t *b;
+ tcp_header_t *th;
+ int i;
+
+ for (i = 0; i < n_bufs; i++)
+ {
+ b = vlib_get_buffer (vm, to_next[i]);
+ if (!(b->flags & VLIB_BUFFER_IS_TRACED))
+ continue;
+ th = vlib_buffer_get_current (b);
+ tc = tcp_connection_get (vnet_buffer (b)->tcp.connection_index,
+ vm->thread_index);
+ t = vlib_add_trace (vm, node, b, sizeof (*t));
+ clib_memcpy_fast (&t->tcp_header, th, sizeof (t->tcp_header));
+ clib_memcpy_fast (&t->tcp_connection, tc, sizeof (t->tcp_connection));
+ }
+}
+
+always_inline void
+tcp_output_push_ip (vlib_main_t * vm, vlib_buffer_t * b0,
+ tcp_connection_t * tc0, u8 is_ip4)
+{
+ TCP_EVT (TCP_EVT_OUTPUT, tc0,
+ ((tcp_header_t *) vlib_buffer_get_current (b0))->flags,
+ b0->current_length);
+
+ if (is_ip4)
+ vlib_buffer_push_ip4 (vm, b0, &tc0->c_lcl_ip4, &tc0->c_rmt_ip4,
+ IP_PROTOCOL_TCP, tcp_csum_offload (tc0));
+ else
+ vlib_buffer_push_ip6_custom (vm, b0, &tc0->c_lcl_ip6, &tc0->c_rmt_ip6,
+ IP_PROTOCOL_TCP, tc0->ipv6_flow_label);
+}
+
+always_inline void
+tcp_check_if_gso (tcp_connection_t * tc, vlib_buffer_t * b)
+{
+ if (PREDICT_TRUE (!(tc->cfg_flags & TCP_CFG_F_TSO)))
+ return;
+
+ u16 data_len = b->current_length - sizeof (tcp_header_t) - tc->snd_opts_len;
+
+ if (PREDICT_FALSE (b->flags & VLIB_BUFFER_TOTAL_LENGTH_VALID))
+ data_len += b->total_length_not_including_first_buffer;
+
+ if (PREDICT_TRUE (data_len <= tc->snd_mss))
+ return;
+ else
+ {
+ ASSERT ((b->flags & VNET_BUFFER_F_L3_HDR_OFFSET_VALID) != 0);
+ ASSERT ((b->flags & VNET_BUFFER_F_L4_HDR_OFFSET_VALID) != 0);
+ b->flags |= VNET_BUFFER_F_GSO;
+ vnet_buffer2 (b)->gso_l4_hdr_sz =
+ sizeof (tcp_header_t) + tc->snd_opts_len;
+ vnet_buffer2 (b)->gso_size = tc->snd_mss;
+ }
+}
+
+always_inline void
+tcp_output_handle_packet (tcp_connection_t * tc0, vlib_buffer_t * b0,
+ vlib_node_runtime_t * error_node, u16 * next0,
+ u8 is_ip4)