/* * Copyright (c) 2016 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. */ /** * @file * @brief Local TCP/IP stack punt infrastructure. * * Provides a set of VPP nodes together with the relevant APIs and CLI * commands in order to adjust and dispatch packets from the VPP data plane * to the local TCP/IP stack */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define foreach_punt_next \ _ (PUNT4, "ip4-punt") \ _ (PUNT6, "ip6-punt") typedef enum { #define _(s,n) PUNT_NEXT_##s, foreach_punt_next #undef _ PUNT_N_NEXT, } punt_next_t; enum punt_socket_rx_next_e { PUNT_SOCKET_RX_NEXT_INTERFACE_OUTPUT, PUNT_SOCKET_RX_NEXT_IP4_LOOKUP, PUNT_SOCKET_RX_NEXT_IP6_LOOKUP, PUNT_SOCKET_RX_N_NEXT }; #define punt_next_punt(is_ip4) (is_ip4 ? PUNT_NEXT_PUNT4 : PUNT_NEXT_PUNT6) extern vlib_node_registration_t udp4_punt_node; extern vlib_node_registration_t udp6_punt_node; extern vlib_node_registration_t udp4_punt_socket_node; extern vlib_node_registration_t udp6_punt_socket_node; static vlib_node_registration_t punt_socket_rx_node; extern punt_main_t punt_main; #ifndef CLIB_MARCH_VARIANT punt_main_t punt_main; char * vnet_punt_get_server_pathname (void) { punt_main_t *pm = &punt_main; return pm->sun_path; } #endif /* CLIB_MARCH_VARIANT */ /** @brief IPv4/IPv6 UDP punt node main loop. This is the main loop inline function for IPv4/IPv6 UDP punt transition node. @param vm vlib_main_t corresponding to the current thread @param node vlib_node_runtime_t @param frame vlib_frame_t whose contents should be dispatched @param is_ipv4 indicates if called for IPv4 or IPv6 node */ always_inline uword udp46_punt_inline (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame, int is_ip4) { u32 n_left_from, *from, *to_next; word advance; from = vlib_frame_vector_args (from_frame); n_left_from = from_frame->n_vectors; /* udp[46]_lookup hands us the data payload, not the IP header */ if (is_ip4) advance = -(sizeof (ip4_header_t) + sizeof (udp_header_t)); else advance = -(sizeof (ip6_header_t) + sizeof (udp_header_t)); while (n_left_from > 0) { u32 n_left_to_next; vlib_get_next_frame (vm, node, punt_next_punt (is_ip4), to_next, n_left_to_next); while (n_left_from > 0 && n_left_to_next > 0) { u32 bi0; vlib_buffer_t *b0; bi0 = from[0]; to_next[0] = bi0; from += 1; to_next += 1; n_left_from -= 1; n_left_to_next -= 1; b0 = vlib_get_buffer (vm, bi0); vlib_buffer_advance (b0, advance); b0->error = node->errors[PUNT_ERROR_UDP_PORT]; } vlib_put_next_frame (vm, node, punt_next_punt (is_ip4), n_left_to_next); } return from_frame->n_vectors; } static char *punt_error_strings[] = { #define punt_error(n,s) s, #include "punt_error.def" #undef punt_error }; /** @brief IPv4 UDP punt node. @node ip4-udp-punt This is the IPv4 UDP punt transition node. It is registered as a next node for the "ip4-udp-lookup" handling UDP port(s) requested for punt. The buffer's current data pointer is adjusted to the original packet IPv4 header. All buffers are dispatched to "error-punt". @param vm vlib_main_t corresponding to the current thread @param node vlib_node_runtime_t @param frame vlib_frame_t whose contents should be dispatched @par Graph mechanics: next index usage @em Sets: - vnet_buffer(b)->current_data - vnet_buffer(b)->current_len Next Index: - Dispatches the packet to the "error-punt" node */ VLIB_NODE_FN (udp4_punt_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { return udp46_punt_inline (vm, node, from_frame, 1 /* is_ip4 */ ); } /** @brief IPv6 UDP punt node. @node ip6-udp-punt This is the IPv6 UDP punt transition node. It is registered as a next node for the "ip6-udp-lookup" handling UDP port(s) requested for punt. The buffer's current data pointer is adjusted to the original packet IPv6 header. All buffers are dispatched to "error-punt". @param vm vlib_main_t corresponding to the current thread @param node vlib_node_runtime_t @param frame vlib_frame_t whose contents should be dispatched @par Graph mechanics: next index usage @em Sets: - vnet_buffer(b)->current_data - vnet_buffer(b)->current_len Next Index: - Dispatches the packet to the "error-punt" node */ VLIB_NODE_FN (udp6_punt_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { return udp46_punt_inline (vm, node, from_frame, 0 /* is_ip4 */ ); } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (udp4_punt_node) = { .name = "ip4-udp-punt", /* Takes a vector of packets. */ .vector_size = sizeof (u32), .n_errors = PUNT_N_ERROR, .error_strings = punt_error_strings, .n_next_nodes = PUNT_N_NEXT, .next_nodes = { #define _(s,n) [PUNT_NEXT_##s] = n, foreach_punt_next #undef _ }, }; VLIB_REGISTER_NODE (udp6_punt_node) = { .name = "ip6-udp-punt", /* Takes a vector of packets. */ .vector_size = sizeof (u32), .n_errors = PUNT_N_ERROR, .error_strings = punt_error_strings, .n_next_nodes = PUNT_N_NEXT, .next_nodes = { #define _(s,n) [PUNT_NEXT_##s] = n, foreach_punt_next #undef _ }, }; /* *INDENT-ON* */ static punt_client_t * punt_client_get (bool is_ip4, u16 port) { punt_main_t *pm = &punt_main; punt_client_t *v = is_ip4 ? pm->clients_by_dst_port4 : pm->clients_by_dst_port6; u16 i = sparse_vec_index (v, port); if (i == SPARSE_VEC_INVALID_INDEX) return 0; return &vec_elt (v, i); } static struct sockaddr_un * punt_socket_get (bool is_ip4, u16 port) { punt_client_t *v = punt_client_get (is_ip4, port); if (v) return &v->caddr; return NULL; } #ifndef CLIB_MARCH_VARIANT static int punt_socket_register (bool is_ip4, u8 protocol, u16 port, char *client_pathname) { punt_main_t *pm = &punt_main; punt_client_t c, *n; punt_client_t *v = is_ip4 ? pm->clients_by_dst_port4 : pm->clients_by_dst_port6; if (strncmp (client_pathname, vnet_punt_get_server_pathname (), UNIX_PATH_MAX) == 0) return -1; clib_memset (&c, 0, sizeof (c)); memcpy (c.caddr.sun_path, client_pathname, sizeof (c.caddr.sun_path)); c.caddr.sun_family = AF_UNIX; c.port = port; c.protocol = protocol; n = sparse_vec_validate (v, port); n[0] = c; return 0; } /* $$$$ Just leaves the mapping in place for now */ static void punt_socket_unregister (bool is_ip4, u8 protocol, u16 port) { return; } #endif /* CLIB_MARCH_VARIANT */ typedef struct { punt_client_t client; u8 is_midchain; } udp_punt_trace_t; static u8 * format_udp_punt_trace (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); udp_punt_trace_t *t = va_arg (*args, udp_punt_trace_t *); u32 indent = format_get_indent (s); s = fo
/*
 * trace_classify.h - Use the classifier to decide if a packet is traced
 *
 * Copyright (c) 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.
 */
#include <vlib/vlib.h>
#include <vnet/vnet.h>
#include <vppinfra/error.h>
#include <vnet/classify/vnet_classify.h>

/** @file trace_classify.h
 * Use the vpp classifier to decide whether to trace packets
 */

/** @brief vnet_is_packet_traced
 * @param vlib_buffer_t *b - packet to classify
 * @param int func - 0 => use classifier w/ supplied table index
 * @param u32 classify_table_index - classifier table index
 * @return 0 => no trace, 1 => trace, -1 => error
 */

static inline int
vnet_is_packet_traced_inline (vlib_buffer_t * b,
			      u32 classify_table_index, int func)
{
  vnet_classify_main_t *vcm = &vnet_classify_main;
  vnet_classify_table_t *t;
  vnet_classify_entry_t *e;
  u64 hash;

  /*$$$ add custom classifiers here, if any */
  if (func != 0)
    return -1;

  /* This will happen... */
  if (pool_is_free_index (vcm->tables, classify_table_index))
    return -1;

  /* Get the table */
  t = pool_elt_at_index (vcm->tables, classify_table_index);

  /* Hash the packet */
  hash = vnet_classify_hash_packet (t, vlib_buffer_get_current (b));

  /* See if there's a matching entry */
  e = vnet_classify_find_entry (t, vlib_buffer_get_current (b), hash,
				0 /* time = 0, disables hit-counter */ );
  /* Hit means trace the packet... */
  if (e)
    {
      /* Manual hit accounting */
      e->hits++;
      return 1;
    }

  /*
   * Look for a hit in a less-specific table.
   * Performance hint: for this use-case, don't go there.
   */
  while (1)
    {
      /* Most likely, we're done right now */
      if (PREDICT_TRUE (t->next_table_index == ~0))
	return 0;
      t = pool_elt_at_index (vcm->tables, t->next_table_index);

      /* Compute hash for this table */
      hash = vnet_classify_hash_packet (t, vlib_buffer_get_current (b));

      /* See if there's a matching entry */
      e = vnet_classify_find_entry (t, vlib_buffer_get_current (b), hash,
				    0 /* time = 0, disables hit-counter */ );
      if (e)
	{
	  /* Manual hit accounting */
	  e->hits++;
	  return 1;
	}
    }
  /* NOTREACHED */
}

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
return (s); } static clib_error_t * punt_socket_show_cmd (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { u8 is_ipv6; punt_main_t *pm = &punt_main; clib_error_t *error = NULL; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "ipv4")) is_ipv6 = 0; else if (unformat (input, "ipv6")) is_ipv6 = 1; else { error = clib_error_return (0, "parse error: '%U'", format_unformat_error, input); goto done; } } punt_client_t *v = is_ipv6 ? pm->clients_by_dst_port6 : pm->clients_by_dst_port4; vlib_cli_output (vm, "%U", format_punt_socket, v, &is_ipv6); done: return (error); } /*? * * @cliexpar * @cliexcmd{show punt socket ipv4} ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_punt_socket_registration_command, static) = { .path = "show punt socket registrations", .function = punt_socket_show_cmd, .short_help = "show punt socket registrations [ipv4|ipv6]", .is_mp_safe = 1, }; /* *INDENT-ON* */ clib_error_t * ip_punt_init (vlib_main_t * vm) { punt_main_t *pm = &punt_main; pm->clients_by_dst_port6 = sparse_vec_new (sizeof (pm->clients_by_dst_port6[0]), BITS (((udp_header_t *) 0)->dst_port)); pm->clients_by_dst_port4 = sparse_vec_new (sizeof (pm->clients_by_dst_port4[0]), BITS (((udp_header_t *) 0)->dst_port)); pm->is_configured = false; pm->interface_output_node = vlib_get_node_by_name (vm, (u8 *) "interface-output"); return 0; } VLIB_INIT_FUNCTION (ip_punt_init); #endif /* CLIB_MARCH_VARIANT */ static clib_error_t * punt_config (vlib_main_t * vm, unformat_input_t * input) { punt_main_t *pm = &punt_main; char *socket_path = 0; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "socket %s", &socket_path)) strncpy (pm->sun_path, socket_path, UNIX_PATH_MAX - 1); else return clib_error_return (0, "unknown input `%U'", format_unformat_error, input); } if (socket_path == 0) return 0; /* UNIX domain socket */ struct sockaddr_un addr; if ((pm->socket_fd = socket (AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) { return clib_error_return (0, "socket error"); } clib_memset (&addr, 0, sizeof (addr)); addr.sun_family = AF_UNIX; if (*socket_path == '\0') { *addr.sun_path = '\0'; strncpy (addr.sun_path + 1, socket_path + 1, sizeof (addr.sun_path) - 2); } else { strncpy (addr.sun_path, socket_path, sizeof (addr.sun_path) - 1); unlink (socket_path); } if (bind (pm->socket_fd, (struct sockaddr *) &addr, sizeof (addr)) == -1) { return clib_error_return (0, "bind error"); } /* Register socket */ clib_file_main_t *fm = &file_main; clib_file_t template = { 0 }; template.read_function = punt_socket_read_ready; template.file_descriptor = pm->socket_fd; template.description = format (0, "%s", socket_path); pm->clib_file_index = clib_file_add (fm, &template); pm->is_configured = true; return 0; } VLIB_CONFIG_FUNCTION (punt_config, "punt"); /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */