/* * Copyright (c) 2017-2019 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SRC_VNET_TCP_TCP_DEBUG_H_ #define SRC_VNET_TCP_TCP_DEBUG_H_ #include /** * Build debugging infra unconditionally. Debug components controlled via * debug configuration. Comes with some overhead so it's not recommended for * production/performance scenarios. Takes priority over TCP_DEBUG_ENABLE. */ #define TCP_DEBUG_ALWAYS (0) /** * Build debugging infra only if enabled. Debug components controlled via * macros that follow. */ #define TCP_DEBUG_ENABLE (0) #define TCP_DEBUG_SM (0) #define TCP_DEBUG_CC (0) #define TCP_DEBUG_CS (0) #define TCP_DEBUG_LC (0 || TCP_DEBUG_SM || TCP_DEBUG_CC || TCP_DEBUG_CS) #define TCP_DEBUG (TCP_DEBUG_ALWAYS || TCP_DEBUG_ENABLE) #define TCP_DEBUG_BUF_ALLOC (0) #if TCP_DEBUG > 0 #define TRANSPORT_DEBUG (1) #endif #define TCP_CONCAT_HELPER(_a, _b) _a##_b #define TCP_CC(_a, _b) TCP_CONCAT_HELPER(_a, _b) #define tcp_evt_lvl(_evt) TCP_CC(_evt, _LVL) #define tcp_evt_grp(_evt) TCP_CC(_evt, _GRP) #define tcp_evt_handler(_evt, _args...) TCP_CC(_evt, _HANDLER) (_args) #define tcp_evt_grp_dbg_lvl(_evt) tcp_dbg_main.grp_dbg_lvl[tcp_evt_grp (_evt)] #define foreach_tcp_evt_grp \ _(LC, "life cycle") \ _(SM, "state machine") \ _(CC, "congestion control") \ _(CS, "cc stats") \ typedef enum tcp_evt_grp_ { #define _(sym, str) TCP_EVT_GRP_ ## sym, foreach_tcp_evt_grp #undef _ TCP_EVT_N_GRP } tcp_evt_grp_e; typedef struct tcp_dbg_main_ { u8 grp_dbg_lvl[TCP_EVT_N_GRP]; u32 *free_track_indices; } tcp_dbg_main_t; extern tcp_dbg_main_t tcp_dbg_main; #define foreach_tcp_dbg_evt \ _(INIT, LC, 1, "init") \ _(DEALLOC, LC, 1, "dealloc") \ _(OPEN, LC, 1, "open") \ _(CLOSE, LC, 1, "close") \ _(BIND, LC, 1, "bind") \ _(UNBIND, LC, 1, "unbind") \ _(DELETE, LC, 1, "delete") \ _(SYN_RCVD, LC, 1, "SYN rcvd") \ _(STATE_CHANGE, LC, 1, "state change") \ _(SYN_SENT, SM, 1, "SYN sent") \ _(SYN_RXT, SM, 1, "SYN retransmit") \ _(SYNACK_SENT, SM, 1, "SYNACK sent") \ _(SYNACK_RCVD, SM, 1, "SYNACK rcvd") \ _(FIN_SENT, SM, 1, "FIN sent") \ _(FIN_RCVD, SM, 1, "FIN rcvd") \ _(RST_SENT, SM, 1, "RST sent") \ _(RST_RCVD, SM, 1, "RST rcvd") \ _(TIMER_POP, SM, 1, "timer pop") \ _(SEG_INVALID, SM, 2, "invalid segment") \ _(PAWS_FAIL, SM, 2, "failed paws check") \ _(ACK_RCV_ERR, SM, 2, "invalid ack") \ _(RCV_WND_SHRUNK, SM, 2, "shrunk rcv_wnd") \ _(ACK_SENT, SM, 3, "ACK sent") \ _(ACK_RCVD, SM, 3, "ACK rcvd") \ _(PKTIZE, SM, 3, "packetize") \ _(INPUT, SM, 3, "in") \ _(OUTPUT, SM, 4, "output") \ _(SND_WND, SM, 4, "snd_wnd update") \ _(CC_EVT, CC, 1, "cc event") \ _(CC_RTX, CC, 2, "retransmit") \ _(CC_PACK, CC, 2, "cc partial ack") \ _(DUPACK_SENT, CC, 2, "DUPACK sent") \ _(DUPACK_RCVD, CC, 2, "DUPACK rcvd") \ _(CC_SCOREBOARD, CC, 2, "scoreboard stats") \ _(CC_SACKS, CC, 2, "snd sacks stats") \ _(CC_INPUT, CC, 2, "ooo data delivered") \ _(CC_STAT, CS, 1, "cc stats") \ _(CC_RTO_STAT, CS, 1, "cc rto stats") \ typedef enum tcp_evt_types_ { #define _(sym, grp, lvl, str) TCP_EVT_##sym, foreach_tcp_dbg_evt #undef _ } tcp_evt_types_e; typedef enum tcp_evt_lvl_ { #define _(sym, grp, lvl, str) TCP_EVT_## sym ## _LVL = lvl, foreach_tcp_dbg_evt #undef _ } tcp_evt_lvl_e; typedef enum tcp_evt_to_grp_ { #define _(sym, grp, lvl, str) TCP_EVT_ ## sym ## _GRP = TCP_EVT_GRP_ ## grp, foreach_tcp_dbg_evt #undef _ } tcp_evt_to_grp_e; #if TCP_DEBUG_ALWAYS > 0 #define TCP_EVT(_evt, _args...) \ if (PREDICT_FALSE (tcp_evt_grp_dbg_lvl (_evt) >= tcp_evt_lvl (_evt))) \ tcp_evt_handler (_evt, _args) #define TCP_DBG(_fmt, _args...) clib_warning (_fmt, ##_args) #elif TCP_DEBUG_ENABLE > 0 #define TCP_EVT(_evt, _args...) tcp_evt_handler(_evt, _args) #define TCP_DBG(_fmt, _args...) clib_warning (_fmt, ##_args) #else #define TCP_EVT(_evt, _args...) #define TCP_DBG(_fmt, _args...) #endif void tcp_evt_track_register (elog_track_t * et); void tcp_debug_init (void); #define TCP_DECLARE_ETD(_tc, _e, _size) \ struct \ { \ u32 data[_size]; \ } * ed; \ ed = ELOG_TRACK_DATA (&vlib_global_main.elog_main, _e, \ _tc->c_elog_track) \ /* * Event handlers definitions */ #if TCP_DEBUG_LC || TCP_DEBUG_ALWAYS /* * Infra and evt track setup */ #define TCP_DBG_IP_TAG_LCL(_tc) \ { \ if (_tc->c_is_ip4) \ { \ ELOG_TYPE_DECLARE (_e) = \ { \ .format = "lcl: %d.%d.%d.%d:%d", \ .format_args = "i4i4i4i4i4", \ }; \ TCP_DECLARE_ETD(_tc, _e, 5); \ ed->data[0] = _tc->c_lcl_ip.ip4.as_u8[0]; \ ed->data[1] = _tc->c_lcl_ip.ip4.as_u8[1]; \ ed->data[2] = _tc->c_lcl_ip.ip4.as_u8[2]; \ ed->data[3] = _tc->c_lcl_ip.ip4.as_u8[3]; \ ed->data[4] = clib_net_to_host_u16(_tc->c_lcl_port); \ } \ } #define TCP_DBG_IP_TAG_RMT(_tc) \ { \ if (_tc->c_is_ip4) \ { \ ELOG_TYPE_DECLARE (_e) = \ { \ .format = "rmt: %d.%d.%d.%d:%d", \ .format_args = "i4i4i4i4i4", \ }; \ TCP_DECLARE_ETD(_tc, _e, 5); \ ed->data[0] = _tc->c_rmt_ip.ip4.as_u8[0]; \ ed->data[1] = _tc->c_rmt_ip.ip4.as_u8[1]; \ ed->data[2] = _tc->c_rmt_ip.ip4.as_u8[2]; \ ed->data[3] = _tc->c_rmt_ip.ip4.as_u8[3]; \ ed->data[4] = clib_net_to_host_u16(_tc->c_rmt_port); \ } \ } #define TCP_EVT_INIT_HANDLER(_tc, _is_l, ...) \ { \ char *_fmt = _is_l ? "l[%d].%d:%d%c" : "[%d].%d:%d->.%d:%d%c"; \ if (_tc->c_is_ip4) \ { \ _tc->c_elog_track.name = \ (char *) format (0, _fmt, _tc->c_thread_index, \ _tc->c_lcl_ip.ip4.as_u8[3], \ clib_net_to_host_u16(_tc->c_lcl_port), \ _tc->c_rmt_ip.ip4.as_u8[3], \ clib_net_to_host_u16(_tc->c_rmt_port), 0); \ } \ else \ _tc->c_elog_track.name = \ (char *) format (0, _fmt, _tc->c_thread_index, \ _tc->c_lcl_ip.ip6.as_u8[15], \ clib_net_to_host_u16(_tc->c_lcl_port), \ _tc->c_rmt_ip.ip6.as_u8[15], \ clib_net_to_host_u16(_tc->c_rmt_port), 0); \ tcp_evt_track_register (&_tc->c_elog_track); \ TCP_DBG_IP_TAG_LCL(_tc); \ TCP_DBG_IP_TAG_RMT(_tc); \ } #define TCP_EVT_DEALLOC_HANDLER(_tc, ...) \ { \ vec_free (_tc->c_elog_track.name); \ vec_add1 (tcp_dbg_main.free_track_indices, \ _tc->c_elog_track.track_index_plus_one - 1); \ } #define TCP_EVT_OPEN_HANDLER(_tc, ...) \ { \ TCP_EVT_INIT_HANDLER(_tc, 0); \ ELOG_TYPE_DECLARE (_e) = \ { \ .format = "open: index %d", \ .format_args = "i4", \ }; \ TCP_DECLARE_ETD(_tc, _e, 1); \ ed->data[0] = _tc->c_c_index; \ } #define TCP_EVT_CLOSE_HANDLER(_tc, ...) \ { \ ELOG_TYPE_DECLARE (_e) = \ { \ .format = "close: cidx %d", \ .format_args = "i4", \ }; \ TCP_DECLARE_ETD(_tc, _e, 1); \ ed->data[0] = _tc->c_c_index; \ } #define TCP_EVT_BIND_HANDLER(_tc, ...) \ { \ TCP_EVT_INIT_HANDLER(_tc, 1); \ ELOG_TYPE_DECLARE (_e) = \ { \ .format = "bind: listener %d", \ }; \ TCP_DECLARE_ETD(_tc, _e, 1); \ ed->data[0] = _tc->c_c_index; \ } #define TCP_EVT_SYN_RCVD_HANDLER(_tc,_init, ...) \ { \ if (_init) \ TCP_EVT_INIT_HANDLER(_tc, 0); \ ELOG_TYPE_DECLARE (_e) = \ { \ .format = "syn-rx: cidx %u sidx %u irs %u", \ .format_args = "i4i4i4", \ }; \ TCP_DECLARE_ETD(_tc, _e, 3); \ ed->data[0] = _tc->c_c_index; \ ed->data[1] = _tc->c_s_index; \ ed->data[2] = _tc->irs; \ TCP_EVT_STATE_CHANGE_HANDLER(_tc); \ } #define TCP_EVT_UNBIND_HANDLER(_tc, ...) \ { \ TCP_EVT_DEALLOC_HANDLER(_tc); \ ELOG_TYPE_DECLARE (_e) = \ { \ .format = "unbind: listener %d", \ }; \ TCP_DECLARE_ETD(_tc, _e, 1); \ ed->data[0] = _tc->c_c_index; \ TCP_EVT_DEALLOC_HANDLER(_tc); \ } #define TCP_EVT_DELETE_HANDLER(_tc, ...) \ { \ ELOG_TYPE_DECLARE (_e) = \ { \ .format = "delete: cidx %d sidx %d", \ .format_args = "i4i4", \ }; \ TCP_DECLARE_ETD(_tc, _e, 2); \ ed->data[0] = _tc->c_c_index; \ ed->data[1] = _tc->c_s_index; \ TCP_EVT_DEALLOC_HANDLER(_tc); \ } #endif /* * State machine */ #if TCP_DEBUG_SM > 0 || TCP_DEBUG_ALWAYS #define TCP_EVT_STATE_CHANGE_HANDLER(_tc, ...) \ { \ ELOG_TYPE_DECLARE (_e) = \ { \ .format = "state: %s", \ .format_args = "t4", \ .n_enum_strings = 11, \ .enum_strings = { \ "closed", \ "listen", \ "syn-sent", \ "syn-rcvd", \ "established", \ "close_wait", \ "fin-wait-1", \ "last-ack", \ "closing", \ "fin-wait-2", \ "time-wait", \ }, \ }; \ TCP_DECLARE_ETD(_tc, _e, 1); \ ed->data[0] = _tc->state; \ } #define TCP_EVT_SYN_SENT_HANDLER(_tc, ...) \ {
/*
 * Copyright (c) 2017 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * ip/ip6_input.c: IP v6 input node
 *
 * Copyright (c) 2008 Eliot Dresselhaus
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef included_ip6_input_h
#define included_ip6_input_h

#include <vnet/ip/ip.h>
#include <vnet/ip/icmp6.h>

extern char *ip6_error_strings[];

typedef enum
{
  IP6_INPUT_NEXT_DROP,
  IP6_INPUT_NEXT_LOOKUP,
  IP6_INPUT_NEXT_LOOKUP_MULTICAST,
  IP6_INPUT_NEXT_ICMP_ERROR,
  IP6_INPUT_N_NEXT,
} ip6_input_next_t;

always_inline void
ip6_input_check_x2 (vlib_main_t * vm,
		    vlib_node_runtime_t * error_node,
		    vlib_buffer_t * p0, vlib_buffer_t * p1,
		    ip6_header_t * ip0, ip6_header_t * ip1,
		    u32 * next0, u32 * next1)
{
  u8 error0, error1;

  error0 = error1 = IP6_ERROR_NONE;

  /* Version != 6?  Drop it. */
  error0 =
    (clib_net_to_host_u32
     (ip0->ip_version_traffic_class_and_flow_label) >> 28) !=
    6 ? IP6_ERROR_VERSION : error0;
  error1 =
    (clib_net_to_host_u32
     (ip1->ip_version_traffic_class_and_flow_label) >> 28) !=
    6 ? IP6_ERROR_VERSION : error1;

  /* hop limit < 1? Drop it.  for link-local broadcast packets,
   * like dhcpv6 packets from client has hop-limit 1, which should not
   * be dropped.
   */
  error0 = ip0->hop_limit < 1 ? IP6_ERROR_TIME_EXPIRED : error0;
  error1 = ip1->hop_limit < 1 ? IP6_ERROR_TIME_EXPIRED : error1;

  /* L2 length must be at least minimal IP header. */
  error0 =
    p0->current_length < sizeof (ip0[0]) ? IP6_ERROR_TOO_SHORT : error0;
  error1 =
    p1->current_length < sizeof (ip1[0]) ? IP6_ERROR_TOO_SHORT : error1;

  if (PREDICT_FALSE (error0 != IP6_ERROR_NONE))
    {
      if (error0 == IP6_ERROR_TIME_EXPIRED)
	{
	  icmp6_error_set_vnet_buffer (p0, ICMP6_time_exceeded,
				       ICMP6_time_exceeded_ttl_exceeded_in_transit,
				       0);
	  *next0 = IP6_INPUT_NEXT_ICMP_ERROR;
	}
      else
	{
	  *next0 = IP6_INPUT_NEXT_DROP;
	}
    }
  if (PREDICT_FALSE (error1 != IP6_ERROR_NONE))
    {
      if (error1 == IP6_ERROR_TIME_EXPIRED)
	{
	  icmp6_error_set_vnet_buffer (p1, ICMP6_time_exceeded,
				       ICMP6_time_exceeded_ttl_exceeded_in_transit,
				       0);
	  *next1 = IP6_INPUT_NEXT_ICMP_ERROR;
	}
      else
	{
	  *next1 = IP6_INPUT_NEXT_DROP;
	}
    }
}

always_inline void
ip6_input_check_x1 (vlib_main_t * vm,
		    vlib_node_runtime_t * error_node,
		    vlib_buffer_t * p0, ip6_header_t * ip0, u32 * next0)
{
  u8 error0;

  error0 = IP6_ERROR_NONE;

  /* Version != 6?  Drop it. */
  error0 =
    (clib_net_to_host_u32
     (ip0->ip_version_traffic_class_and_flow_label) >> 28) !=
    6 ? IP6_ERROR_VERSION : error0;

  /* hop limit < 1? Drop it.  for link-local broadcast packets,
   * like dhcpv6 packets from