VPP-1506: dump local punts and registered punt sockets
[vpp.git] / src / vnet / ip / punt.c
index d8c7d81..c0902f3 100644 (file)
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/socket.h>
-#include <sys/un.h>
 #include <sys/uio.h>
 #include <stdlib.h>
-#include <stdbool.h>
 
 #define foreach_punt_next                      \
   _ (PUNT4, "ip4-punt")                         \
@@ -261,7 +259,7 @@ punt_socket_get (bool is_ip4, u16 port)
   return NULL;
 }
 
-static void
+static int
 punt_socket_register (bool is_ip4, u8 protocol, u16 port,
                      char *client_pathname)
 {
@@ -270,12 +268,18 @@ punt_socket_register (bool is_ip4, u8 protocol, u16 port,
   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 */
@@ -424,6 +428,9 @@ udp46_punt_socket_inline (vlib_main_t * vm,
       if (sendmsg (pm->socket_fd, &msg, 0) < (ssize_t) l)
        vlib_node_increment_counter (vm, node_index,
                                     PUNT_ERROR_SOCKET_TX_ERROR, 1);
+      else
+       vlib_node_increment_counter (vm, node_index, PUNT_ERROR_SOCKET_TX, 1);
+
     }
 
 error:
@@ -597,17 +604,25 @@ punt_socket_rx (vlib_main_t * vm,
   return total_count;
 }
 
+/* *INDENT-OFF* */
 VLIB_REGISTER_NODE (punt_socket_rx_node, static) =
 {
-  .function = punt_socket_rx,.name = "punt-socket-rx",.type =
-    VLIB_NODE_TYPE_INPUT,.state = VLIB_NODE_STATE_INTERRUPT,.vector_size =
-    1,.n_errors = PUNT_N_ERROR,.error_strings =
-    punt_error_strings,.n_next_nodes = PUNT_SOCKET_RX_N_NEXT,.next_nodes =
-  {
-[PUNT_SOCKET_RX_NEXT_INTERFACE_OUTPUT] = "interface-output",
-      [PUNT_SOCKET_RX_NEXT_IP4_LOOKUP] = "ip4-lookup",
-      [PUNT_SOCKET_RX_NEXT_IP6_LOOKUP] = "ip6-lookup",},.format_trace =
-    format_punt_trace,};
+ .function = punt_socket_rx,
+ .name = "punt-socket-rx",
+ .type = VLIB_NODE_TYPE_INPUT,
+ .state = VLIB_NODE_STATE_INTERRUPT,
+ .vector_size = 1,
+ .n_errors = PUNT_N_ERROR,
+ .error_strings = punt_error_strings,
+ .n_next_nodes = PUNT_SOCKET_RX_N_NEXT,
+ .next_nodes = {
+               [PUNT_SOCKET_RX_NEXT_INTERFACE_OUTPUT] = "interface-output",
+               [PUNT_SOCKET_RX_NEXT_IP4_LOOKUP] = "ip4-lookup",
+               [PUNT_SOCKET_RX_NEXT_IP6_LOOKUP] = "ip6-lookup",
+               },
+ .format_trace = format_punt_trace,
+};
+/* *INDENT-ON* */
 
 static clib_error_t *
 punt_socket_read_ready (clib_file_t * uf)
@@ -645,7 +660,10 @@ vnet_punt_socket_add (vlib_main_t * vm, u32 header_version,
     return clib_error_return (0, "UDP port number required");
 
   /* Register client */
-  punt_socket_register (is_ip4, protocol, port, client_pathname);
+  if (punt_socket_register (is_ip4, protocol, port, client_pathname) < 0)
+    return clib_error_return (0,
+                             "Punt socket: Invalid client path: %s",
+                             client_pathname);
 
   u32 node_index = is_ip4 ? udp4_punt_socket_node.index :
     udp6_punt_socket_node.index;
@@ -748,7 +766,7 @@ static clib_error_t *
 punt_cli (vlib_main_t * vm,
          unformat_input_t * input, vlib_cli_command_t * cmd)
 {
-  u32 port;
+  u32 port = ~0;
   bool is_add = true;
   u32 protocol = ~0;
   clib_error_t *error = NULL;
@@ -758,25 +776,9 @@ punt_cli (vlib_main_t * vm,
       if (unformat (input, "del"))
        is_add = false;
       else if (unformat (input, "all"))
-       {
-         /* punt both IPv6 and IPv4 when used in CLI */
-         error = vnet_punt_add_del (vm, ~0, protocol, ~0, is_add);
-         if (error)
-           {
-             clib_error_report (error);
-             goto done;
-           }
-       }
+       ;
       else if (unformat (input, "%d", &port))
-       {
-         /* punt both IPv6 and IPv4 when used in CLI */
-         error = vnet_punt_add_del (vm, ~0, protocol, port, is_add);
-         if (error)
-           {
-             clib_error_report (error);
-             goto done;
-           }
-       }
+       ;
       else if (unformat (input, "udp"))
        protocol = IP_PROTOCOL_UDP;
       else if (unformat (input, "tcp"))
@@ -788,6 +790,15 @@ punt_cli (vlib_main_t * vm,
          goto done;
        }
     }
+
+  /* punt both IPv6 and IPv4 when used in CLI */
+  error = vnet_punt_add_del (vm, ~0, protocol, port, is_add);
+  if (error)
+    {
+      clib_error_report (error);
+      goto done;
+    }
+
 done:
   return error;
 }
@@ -820,6 +831,228 @@ VLIB_CLI_COMMAND (punt_command, static) = {
 };
 /* *INDENT-ON* */
 
+static clib_error_t *
+punt_socket_register_cmd (vlib_main_t * vm,
+                         unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  bool is_ipv4 = true;
+  u32 protocol = ~0;
+  u32 port = ~0;
+  u8 *socket_name = 0;
+  clib_error_t *error = NULL;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "ipv4"))
+       ;
+      else if (unformat (input, "ipv6"))
+       is_ipv4 = false;
+      else if (unformat (input, "udp"))
+       protocol = IP_PROTOCOL_UDP;
+      else if (unformat (input, "tcp"))
+       protocol = IP_PROTOCOL_TCP;
+      else if (unformat (input, "%d", &port))
+       ;
+      else if (unformat (input, "socket %s", &socket_name))
+       ;
+      else
+       {
+         error = clib_error_return (0, "parse error: '%U'",
+                                    format_unformat_error, input);
+         goto done;
+       }
+    }
+
+  error =
+    vnet_punt_socket_add (vm, 1, is_ipv4, protocol, port,
+                         (char *) socket_name);
+  if (error)
+    {
+      goto done;
+    }
+done:
+  return error;
+}
+
+/*?
+ *
+ * @cliexpar
+ * @cliexcmd{punt socket register}
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (punt_socket_register_command, static) =
+{
+  .path = "punt socket register",
+  .function = punt_socket_register_cmd,
+  .short_help = "punt socket register [ipv4|ipv6] [udp|tcp]> <all | port-num1 [port-num2 ...]> <socket>",
+  .is_mp_safe = 1,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+punt_socket_deregister_cmd (vlib_main_t * vm,
+                           unformat_input_t * input,
+                           vlib_cli_command_t * cmd)
+{
+  bool is_ipv4 = true;
+  u32 protocol = ~0;
+  u32 port = ~0;
+  clib_error_t *error = NULL;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "ipv4"))
+       ;
+      else if (unformat (input, "ipv6"))
+       is_ipv4 = false;
+      else if (unformat (input, "udp"))
+       protocol = IP_PROTOCOL_UDP;
+      else if (unformat (input, "tcp"))
+       protocol = IP_PROTOCOL_TCP;
+      else if (unformat (input, "%d", &port))
+       ;
+      else
+       {
+         error = clib_error_return (0, "parse error: '%U'",
+                                    format_unformat_error, input);
+         goto done;
+       }
+    }
+
+  error = vnet_punt_socket_del (vm, is_ipv4, protocol, port);
+  if (error)
+    {
+      goto done;
+    }
+done:
+  return error;
+}
+
+/*?
+ *
+ * @cliexpar
+ * @cliexcmd{punt socket register}
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (punt_socket_deregister_command, static) =
+{
+  .path = "punt socket deregister",
+  .function = punt_socket_deregister_cmd,
+  .short_help = "punt socket deregister [ipv4|ipv6] [udp|tcp]> <all | port-num1 [port-num2 ...]>",
+  .is_mp_safe = 1,
+};
+/* *INDENT-ON* */
+
+punt_socket_detail_t *
+punt_socket_entries (u8 ipv)
+{
+  punt_main_t *pm = &punt_main;
+  punt_client_t *pc;
+  punt_socket_detail_t *ps = 0;
+  bool is_valid;
+
+  punt_client_t *v = !ipv ? pm->clients_by_dst_port4 :
+    pm->clients_by_dst_port6;
+
+  vec_foreach (pc, v)
+  {
+    if (pc && pc->port != 0)
+      {
+       is_valid = false;
+       if (pc->protocol == IP_PROTOCOL_UDP)
+         {
+           is_valid = udp_is_valid_dst_port (pc->port, !ipv);
+         }
+       if (is_valid)
+         {
+           punt_socket_detail_t detail = {
+             .ipv = ipv,
+             .l4_protocol = pc->protocol,
+             .l4_port = pc->port
+           };
+           memcpy (detail.pathname, pc->caddr.sun_path,
+                   sizeof (pc->caddr.sun_path));
+           vec_add1 (ps, detail);
+         }
+      }
+  }
+  return ps;
+}
+
+u8 *
+format_punt_socket (u8 * s, va_list * args)
+{
+  punt_client_t *clients = va_arg (*args, punt_client_t *);
+  u8 *is_ipv6 = va_arg (*args, u8 *);
+  punt_client_t *pc;
+  bool is_valid;
+
+  vec_foreach (pc, clients)
+  {
+    if (pc && pc->port != 0)
+      {
+       is_valid = false;
+       if (pc->protocol == IP_PROTOCOL_UDP)
+         {
+           is_valid = udp_is_valid_dst_port (pc->port, !(*is_ipv6));
+         }
+       if (is_valid)
+         {
+           s = format (s, " punt %s port %d to socket %s \n",
+                       (pc->protocol == IP_PROTOCOL_UDP) ? "UDP" : "TCP",
+                       pc->port, pc->caddr.sun_path);
+         }
+      }
+  }
+
+  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 *
 punt_init (vlib_main_t * vm)
 {
@@ -850,7 +1083,7 @@ punt_config (vlib_main_t * vm, unformat_input_t * input)
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
       if (unformat (input, "socket %s", &socket_path))
-       strncpy (pm->sun_path, socket_path, 108 - 1);
+       strncpy (pm->sun_path, socket_path, UNIX_PATH_MAX - 1);
       else
        return clib_error_return (0, "unknown input `%U'",
                                  format_unformat_error, input);