VPP-1506: dump local punts and registered punt sockets 66/16366/5
authorPavel Kotucek <pavel.kotucek@pantheon.tech>
Wed, 28 Nov 2018 06:42:11 +0000 (07:42 +0100)
committerOle Trøan <otroan@employees.org>
Fri, 7 Dec 2018 12:24:52 +0000 (12:24 +0000)
Change-Id: If7835e9b80ec9402404bfc8d271eb11a10ef992b
Signed-off-by: Pavel Kotucek <pavel.kotucek@pantheon.tech>
13 files changed:
src/vat/api_format.c
src/vnet/ip/punt.api
src/vnet/ip/punt.c
src/vnet/ip/punt.h
src/vnet/ip/punt_api.c
src/vnet/sctp/sctp.c
src/vnet/udp/udp.c
src/vnet/udp/udp.h
src/vnet/udp/udp_local.c
src/vpp/api/custom_dump.c
test/framework.py
test/test_punt.py [new file with mode: 0644]
test/vpp_papi_provider.py

index f54aa02..7f4dabe 100644 (file)
@@ -5572,7 +5572,7 @@ _(ip_source_and_port_range_check_add_del_reply)         \
 _(ip_source_and_port_range_check_interface_add_del_reply)\
 _(delete_subif_reply)                                   \
 _(l2_interface_pbb_tag_rewrite_reply)                   \
-_(punt_reply)                                           \
+_(set_punt_reply)                                       \
 _(feature_enable_disable_reply)                                \
 _(sw_interface_tag_add_del_reply)                      \
 _(hw_interface_set_mtu_reply)                           \
@@ -5902,7 +5902,7 @@ _(IPSEC_GRE_ADD_DEL_TUNNEL_REPLY, ipsec_gre_add_del_tunnel_reply)       \
 _(IPSEC_GRE_TUNNEL_DETAILS, ipsec_gre_tunnel_details)                   \
 _(DELETE_SUBIF_REPLY, delete_subif_reply)                               \
 _(L2_INTERFACE_PBB_TAG_REWRITE_REPLY, l2_interface_pbb_tag_rewrite_reply) \
-_(PUNT_REPLY, punt_reply)                                               \
+_(SET_PUNT_REPLY, set_punt_reply)                                       \
 _(IP_FIB_DETAILS, ip_fib_details)                                       \
 _(IP6_FIB_DETAILS, ip6_fib_details)                                     \
 _(FEATURE_ENABLE_DISABLE_REPLY, feature_enable_disable_reply)           \
@@ -21725,10 +21725,10 @@ api_ipsec_gre_add_del_tunnel (vat_main_t * vam)
 }
 
 static int
-api_punt (vat_main_t * vam)
+api_set_punt (vat_main_t * vam)
 {
   unformat_input_t *i = vam->input;
-  vl_api_punt_t *mp;
+  vl_api_set_punt_t *mp;
   u32 ipv = ~0;
   u32 protocol = ~0;
   u32 port = ~0;
@@ -21752,12 +21752,12 @@ api_punt (vat_main_t * vam)
        }
     }
 
-  M (PUNT, mp);
+  M (SET_PUNT, mp);
 
   mp->is_add = (u8) is_add;
-  mp->ipv = (u8) ipv;
-  mp->l4_protocol = (u8) protocol;
-  mp->l4_port = htons ((u16) port);
+  mp->punt.ipv = (u8) ipv;
+  mp->punt.l4_protocol = (u8) protocol;
+  mp->punt.l4_port = htons ((u16) port);
 
   S (mp);
   W (ret);
@@ -24033,7 +24033,7 @@ _(l2_interface_pbb_tag_rewrite,                                         \
   "<intfc> | sw_if_index <nn> \n"                                       \
   "[disable | push | pop | translate_pbb_stag <outer_tag>] \n"          \
   "dmac <mac> smac <mac> sid <nn> [vlanid <nn>]")                       \
-_(punt, "protocol <l4-protocol> [ip <ver>] [port <l4-port>] [del]")     \
+_(set_punt, "protocol <l4-protocol> [ip <ver>] [port <l4-port>] [del]")     \
 _(flow_classify_set_interface,                                          \
   "<intfc> | sw_if_index <nn> [ip4-table <nn>] [ip6-table <nn>] [del]") \
 _(flow_classify_dump, "type [ip4|ip6]")                                 \
index 4477e5c..c8b222a 100644 (file)
  * limitations under the License.
  */
 
-option version = "1.0.0";
+option version = "2.0.0";
+
+/** \brief Punt definition
+    @param ipv - L3 protocol 4 - IPv4, 6 - IPv6, ~0 - All
+    @param l4_protocol - L4 protocol to be punted
+    @param l4_port - TCP/UDP port to be punted
+*/
+typeonly define punt
+{
+    u8 ipv;
+    u8 l4_protocol;
+    u16 l4_port;
+};
 
 /** \brief Punt traffic to the host
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
     @param is_add - add punt if non-zero, else delete
-    @param ipv - L3 protocol 4 - IPv4, 6 - IPv6, ~0 - All
-    @param l4_protocol - L4 protocol to be punted, only UDP (0x11) is supported
-    @param l4_port - TCP/UDP port to be punted
+    @param punt - punt definition, only UDP (0x11) is supported
 */
-autoreply define punt {
+autoreply define set_punt {
     u32 client_index;
     u32 context;
     u8 is_add;
-    u8 ipv;
-    u8 l4_protocol;
-    u16 l4_port;
+    vl_api_punt_t punt;
+};
+
+define punt_dump
+{
+    u32 client_index;
+    u32 context;
+    u8 is_ipv6;
+};
+
+define punt_details
+{
+    u32 context;
+    vl_api_punt_t punt;
 };
 
 /** \brief Punt traffic to the host via socket
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
     @param header_version - expected meta data header version (currently 1)
-    @param is_ip4 - L3 protocol 1 - IPv4, 0 - IPv6
-    @param l4_protocol - L4 protocol to be punted, only UDP (0x11) is supported
-    @param l4_port - TCP/UDP port to be punted
+    @param punt - punt definition
 */
 define punt_socket_register {
     u32 client_index;
     u32 context;
     u32 header_version;
-    u8 is_ip4;
-    u8 l4_protocol;
-    u16 l4_port;
+    vl_api_punt_t punt;
     u8 pathname[108]; /* Linux sun_path defined to be 108 bytes, see unix(7) */
 };
 
 define punt_socket_register_reply
 {
-  u32 context;
-  i32 retval;
-  u8 pathname[64];
+    u32 context;
+    i32 retval;
+    u8 pathname[64];
+};
+
+define punt_socket_dump
+{
+    u32 client_index;
+    u32 context;
+    u8 is_ipv6;
+};
+
+define punt_socket_details
+{
+    u32 context;
+    vl_api_punt_t punt;
+    u8 pathname[108];
 };
 
 autoreply define punt_socket_deregister {
     u32 client_index;
     u32 context;
-    u8 is_ip4;
-    u8 l4_protocol;
-    u16 l4_port;
+    vl_api_punt_t punt;
 };
 
 /*
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);
index 4cef527..0518b2b 100644 (file)
@@ -20,7 +20,7 @@
 #ifndef included_punt_h
 #define included_punt_h
 
-#include <sys/un.h>
+#include <linux/un.h>
 #include <stdbool.h>
 
 typedef enum
@@ -64,6 +64,7 @@ typedef struct __attribute__ ((packed))
  */
 typedef struct
 {
+  u8 protocol;
   u16 port;
   struct sockaddr_un caddr;
 } punt_client_t;
@@ -82,6 +83,15 @@ typedef struct
 } punt_main_t;
 extern punt_main_t punt_main;
 
+typedef struct punt_socket_detail_t_
+{
+  u8 ipv;
+  u8 l4_protocol;
+  u16 l4_port;
+  u8 pathname[108];
+} punt_socket_detail_t;
+
+punt_socket_detail_t *punt_socket_entries (u8 ipv);
 #endif
 
 /*
index 55ab838..3ca9621 100644 (file)
 #include <vlibapi/api_helper_macros.h>
 
 #define foreach_punt_api_msg                                            \
-_(PUNT, punt)                                                           \
+_(SET_PUNT, set_punt)                                                   \
 _(PUNT_SOCKET_REGISTER, punt_socket_register)                           \
-_(PUNT_SOCKET_DEREGISTER, punt_socket_deregister)
+_(PUNT_SOCKET_DEREGISTER, punt_socket_deregister)                       \
+_(PUNT_DUMP, punt_dump)                                                 \
+_(PUNT_SOCKET_DUMP, punt_socket_dump)
 
 static void
-vl_api_punt_t_handler (vl_api_punt_t * mp)
+vl_api_set_punt_t_handler (vl_api_set_punt_t * mp)
 {
-  vl_api_punt_reply_t *rmp;
+  vl_api_set_punt_reply_t *rmp;
   vlib_main_t *vm = vlib_get_main ();
   int rv = 0;
   clib_error_t *error;
 
-  error = vnet_punt_add_del (vm, mp->ipv, mp->l4_protocol,
-                            ntohs (mp->l4_port), mp->is_add);
+  error = vnet_punt_add_del (vm, mp->punt.ipv, mp->punt.l4_protocol,
+                            ntohs (mp->punt.l4_port), mp->is_add);
   if (error)
     {
       rv = -1;
       clib_error_report (error);
     }
 
-  REPLY_MACRO (VL_API_PUNT_REPLY);
+  REPLY_MACRO (VL_API_SET_PUNT_REPLY);
+}
+
+static void
+vl_api_punt_dump_t_handler (vl_api_punt_dump_t * mp)
+{
+
 }
 
 static void
@@ -72,9 +80,10 @@ vl_api_punt_socket_register_t_handler (vl_api_punt_socket_register_t * mp)
   clib_error_t *error;
   vl_api_registration_t *reg;
 
-  error = vnet_punt_socket_add (vm, ntohl (mp->header_version),
-                               mp->is_ip4, mp->l4_protocol,
-                               ntohs (mp->l4_port), (char *) mp->pathname);
+  error = 0;
+  vnet_punt_socket_add (vm, ntohl (mp->header_version),
+                       mp->punt.ipv, mp->punt.l4_protocol,
+                       ntohs (mp->punt.l4_port), (char *) mp->pathname);
   if (error)
     {
       rv = -1;
@@ -95,6 +104,48 @@ vl_api_punt_socket_register_t_handler (vl_api_punt_socket_register_t * mp)
   vl_api_send_msg (reg, (u8 *) rmp);
 }
 
+void
+send_punt_socket_details (vl_api_registration_t * reg,
+                         u32 context, punt_socket_detail_t * p)
+{
+  vl_api_punt_socket_details_t *mp;
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  if (!mp)
+    return;
+
+  clib_memset (mp, 0, sizeof (*mp));
+  mp->_vl_msg_id = ntohs (VL_API_PUNT_SOCKET_DETAILS);
+  mp->context = context;
+  mp->punt.ipv = p->ipv;
+  mp->punt.l4_protocol = p->l4_protocol;
+  mp->punt.l4_port = htons (p->l4_port);
+  memcpy (mp->pathname, p->pathname, sizeof (p->pathname));
+
+  vl_api_send_msg (reg, (u8 *) mp);
+}
+
+static void
+vl_api_punt_socket_dump_t_handler (vl_api_punt_socket_dump_t * mp)
+{
+  vl_api_registration_t *reg;
+  punt_socket_detail_t *p, *ps;
+  int rv __attribute__ ((unused)) = 0;
+
+  reg = vl_api_client_index_to_registration (mp->client_index);
+  if (!reg)
+    return;
+
+  ps = punt_socket_entries (mp->is_ipv6);
+  /* *INDENT-OFF* */
+  vec_foreach (p, ps)
+  {
+    send_punt_socket_details (reg, mp->context, p);
+  }
+  /* *INDENT-ON* */
+  vec_free (ps);
+}
+
 static void
 vl_api_punt_socket_deregister_t_handler (vl_api_punt_socket_deregister_t * mp)
 {
@@ -104,8 +155,8 @@ vl_api_punt_socket_deregister_t_handler (vl_api_punt_socket_deregister_t * mp)
   clib_error_t *error;
   vl_api_registration_t *reg;
 
-  error = vnet_punt_socket_del (vm, mp->is_ip4, mp->l4_protocol,
-                               ntohs (mp->l4_port));
+  error = vnet_punt_socket_del (vm, mp->punt.ipv, mp->punt.l4_protocol,
+                               ntohs (mp->punt.l4_port));
   if (error)
     {
       rv = -1;
index 41548bc..85ca9b8 100644 (file)
@@ -987,6 +987,29 @@ sctp_init (vlib_main_t * vm)
 
 VLIB_INIT_FUNCTION (sctp_init);
 
+static clib_error_t *
+show_sctp_punt_fn (vlib_main_t * vm, unformat_input_t * input,
+                  vlib_cli_command_t * cmd_arg)
+{
+  sctp_main_t *tm = &sctp_main;
+  if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    return clib_error_return (0, "unknown input `%U'", format_unformat_error,
+                             input);
+  vlib_cli_output (vm, "IPv4 UDP punt: %s",
+                  tm->punt_unknown4 ? "enabled" : "disabled");
+  vlib_cli_output (vm, "IPv6 UDP punt: %s",
+                  tm->punt_unknown6 ? "enabled" : "disabled");
+  return 0;
+}
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_tcp_punt_command, static) =
+{
+  .path = "show sctp punt",
+  .short_help = "show sctp punt",
+  .function = show_sctp_punt_fn,
+};
+/* *INDENT-ON* */
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index ccac921..8c2daaf 100644 (file)
@@ -441,6 +441,66 @@ udp_init (vlib_main_t * vm)
 
 VLIB_INIT_FUNCTION (udp_init);
 
+static clib_error_t *
+show_udp_punt_fn (vlib_main_t * vm, unformat_input_t * input,
+                 vlib_cli_command_t * cmd_arg)
+{
+  udp_main_t *um = vnet_get_udp_main ();
+
+  clib_error_t *error = NULL;
+
+  if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    return clib_error_return (0, "unknown input `%U'", format_unformat_error,
+                             input);
+
+  udp_dst_port_info_t *port_info;
+  if (um->punt_unknown4)
+    {
+      vlib_cli_output (vm, "IPv4 UDP punt: enabled");
+    }
+  else
+    {
+      u8 *s = NULL;
+      vec_foreach (port_info, um->dst_port_infos[UDP_IP4])
+      {
+       if (udp_is_valid_dst_port (port_info->dst_port, 1))
+         {
+           s = format (s, (!s) ? "%d" : ", %d", port_info->dst_port);
+         }
+      }
+      s = format (s, "%c", 0);
+      vlib_cli_output (vm, "IPV4 UDP ports punt : %s", s);
+    }
+
+  if (um->punt_unknown6)
+    {
+      vlib_cli_output (vm, "IPv6 UDP punt: enabled");
+    }
+  else
+    {
+      u8 *s = NULL;
+      vec_foreach (port_info, um->dst_port_infos[UDP_IP6])
+      {
+       if (udp_is_valid_dst_port (port_info->dst_port, 01))
+         {
+           s = format (s, (!s) ? "%d" : ", %d", port_info->dst_port);
+         }
+      }
+      s = format (s, "%c", 0);
+      vlib_cli_output (vm, "IPV6 UDP ports punt : %s", s);
+    }
+
+  return (error);
+}
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_tcp_punt_command, static) =
+{
+  .path = "show udp punt",
+  .short_help = "show udp punt [ipv4|ipv6]",
+  .function = show_udp_punt_fn,
+};
+/* *INDENT-ON* */
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 8b94a00..19db4f8 100644 (file)
@@ -252,6 +252,7 @@ void udp_register_dst_port (vlib_main_t * vm,
                            u32 node_index, u8 is_ip4);
 void udp_unregister_dst_port (vlib_main_t * vm,
                              udp_dst_port_t dst_port, u8 is_ip4);
+bool udp_is_valid_dst_port (udp_dst_port_t dst_port, u8 is_ip4);
 
 void udp_punt_unknown (vlib_main_t * vm, u8 is_ip4, u8 is_add);
 
index bb7305d..a16ba22 100644 (file)
@@ -549,6 +549,22 @@ udp_unregister_dst_port (vlib_main_t * vm, udp_dst_port_t dst_port, u8 is_ip4)
   n[0] = SPARSE_VEC_INVALID_INDEX;
 }
 
+bool
+udp_is_valid_dst_port (udp_dst_port_t dst_port, u8 is_ip4)
+{
+  udp_main_t *um = &udp_main;
+  u16 *n;
+
+  if (is_ip4)
+    n = sparse_vec_validate (um->next_by_dst_port4,
+                            clib_host_to_net_u16 (dst_port));
+  else
+    n = sparse_vec_validate (um->next_by_dst_port6,
+                            clib_host_to_net_u16 (dst_port));
+
+  return (n[0] != SPARSE_VEC_INVALID_INDEX);
+}
+
 void
 udp_punt_unknown (vlib_main_t * vm, u8 is_ip4, u8 is_add)
 {
index 8339721..41a24ef 100644 (file)
@@ -3363,19 +3363,19 @@ static void *vl_api_flow_classify_set_interface_t_print
 }
 
 static void *
-vl_api_punt_t_print (vl_api_punt_t * mp, void *handle)
+vl_api_set_punt_t_print (vl_api_set_punt_t * mp, void *handle)
 {
   u8 *s;
 
   s = format (0, "SCRIPT: punt ");
 
-  if (mp->ipv != (u8) ~ 0)
-    s = format (s, "ip %d ", mp->ipv);
+  if (mp->punt.ipv != (u8) ~ 0)
+    s = format (s, "ip %d ", mp->punt.ipv);
 
-  s = format (s, "protocol %d ", mp->l4_protocol);
+  s = format (s, "protocol %d ", mp->punt.l4_protocol);
 
-  if (mp->l4_port != (u16) ~ 0)
-    s = format (s, "port %d ", ntohs (mp->l4_port));
+  if (mp->punt.l4_port != (u16) ~ 0)
+    s = format (s, "port %d ", ntohs (mp->punt.l4_port));
 
   if (!mp->is_add)
     s = format (s, "del ");
@@ -3891,7 +3891,7 @@ _(IPSEC_GRE_ADD_DEL_TUNNEL, ipsec_gre_add_del_tunnel)                   \
 _(IPSEC_GRE_TUNNEL_DUMP, ipsec_gre_tunnel_dump)                         \
 _(DELETE_SUBIF, delete_subif)                                           \
 _(L2_INTERFACE_PBB_TAG_REWRITE, l2_interface_pbb_tag_rewrite)           \
-_(PUNT, punt)                                                           \
+_(SET_PUNT, set_punt)                                                   \
 _(FLOW_CLASSIFY_SET_INTERFACE, flow_classify_set_interface)             \
 _(FLOW_CLASSIFY_DUMP, flow_classify_dump)                              \
 _(GET_FIRST_MSG_ID, get_first_msg_id)                                   \
index 6c1ba5e..8a1bfcb 100644 (file)
@@ -193,6 +193,8 @@ class VppTestCase(unittest.TestCase):
     classes. It provides methods to create and run test case.
     """
 
+    extra_vpp_punt_config = []
+
     @property
     def packet_infos(self):
         """List of packet infos"""
@@ -308,6 +310,8 @@ class VppTestCase(unittest.TestCase):
                            "{", "plugin", "dpdk_plugin.so", "{", "disable",
                            "}", "plugin", "unittest_plugin.so", "{", "enable",
                            "}", "}", ]
+        if cls.extra_vpp_punt_config is not None:
+            cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
         if plugin_path is not None:
             cls.vpp_cmdline.extend(["plugin_path", plugin_path])
         cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
diff --git a/test/test_punt.py b/test/test_punt.py
new file mode 100644 (file)
index 0000000..c31bdcf
--- /dev/null
@@ -0,0 +1,309 @@
+#!/usr/bin/env python
+import binascii
+import random
+import socket
+import unittest
+import os
+import scapy.layers.inet6 as inet6
+
+from util import ppp, ppc
+from re import compile
+from scapy.packet import Raw
+from scapy.layers.l2 import Ether
+from scapy.layers.inet import IP, UDP, ICMP
+from scapy.layers.inet6 import IPv6, ICMPv6DestUnreach
+from framework import VppTestCase, VppTestRunner
+
+
+class TestPuntSocket(VppTestCase):
+    """ Punt Socket """
+
+    tempdir = ""
+    sock = None
+    err_ptr = compile(r"^([\d]+)\s+([-\w]+)\s+([ -\.\w)(]+)$")
+
+    @classmethod
+    def setUpConstants(cls):
+        tempdir = cls.tempdir
+        cls.extra_vpp_punt_config = [
+            "punt", "{", "socket", cls.tempdir+"/socket_punt", "}"]
+        super(TestPuntSocket, cls).setUpConstants()
+
+    def process_cli(self, exp, ptr):
+        for line in self.vapi.cli(exp).split('\n')[1:]:
+            m = ptr.match(line.strip())
+            if m:
+                yield m.groups()
+
+    def show_errors(self):
+        for pack in self.process_cli("show errors", self.err_ptr):
+            try:
+                count, node, reason = pack
+            except ValueError:
+                pass
+            else:
+                yield count, node, reason
+
+    def get_punt_count(self, counter):
+        errors = list(self.show_errors())
+        for count, node, reason in errors:
+            if (node == counter and
+                    reason == u'Socket TX'):
+                return int(count)
+        return 0
+
+    def socket_client_create(self, sock_name):
+        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+        try:
+            os.unlink(sock_name)
+        except:
+            self.logger.debug("Unlink socket faild")
+        self.sock.bind(sock_name)
+
+    def socket_client_close(self):
+        self.sock.close()
+
+
+class TestIP4PuntSocket(TestPuntSocket):
+    """ Punt Socket for IPv4 """
+
+    def setUp(self):
+        super(TestIP4PuntSocket, self).setUp()
+
+        self.create_pg_interfaces(range(2))
+
+        for i in self.pg_interfaces:
+            i.admin_up()
+            i.config_ip4()
+            i.resolve_arp()
+
+    def tearDown(self):
+        super(TestIP4PuntSocket, self).tearDown()
+        for i in self.pg_interfaces:
+            i.unconfig_ip4()
+            i.admin_down()
+
+    def test_punt_socket_dump(self):
+        """ Punt socket registration"""
+
+        punts = self.vapi.punt_socket_dump(0)
+        self.assertEqual(len(punts), 0)
+
+        #
+        # configure a punt socket
+        #
+        self.vapi.punt_socket_register(1111, self.tempdir+"/socket_punt_1111")
+        self.vapi.punt_socket_register(2222, self.tempdir+"/socket_punt_2222")
+        punts = self.vapi.punt_socket_dump(0)
+        self.assertEqual(len(punts), 2)
+        self.assertEqual(punts[0].punt.l4_port, 1111)
+        # self.assertEqual(punts[0].pathname, "/tmp/punt_socket_udp_1234")
+        self.assertEqual(punts[1].punt.l4_port, 2222)
+        # self.assertEqual(punts[1].pathname, "/tmp/punt_socket_udp_5678")
+
+        #
+        # deregister a punt socket
+        #
+        self.vapi.punt_socket_deregister(1111)
+        punts = self.vapi.punt_socket_dump(0)
+        self.assertEqual(len(punts), 1)
+
+        #
+        # configure a punt socket again
+        #
+        self.vapi.punt_socket_register(1111, self.tempdir+"/socket_punt_1111")
+        self.vapi.punt_socket_register(3333, self.tempdir+"/socket_punt_3333")
+        punts = self.vapi.punt_socket_dump(0)
+        self.assertEqual(len(punts), 3)
+
+        #
+        # deregister all punt socket
+        #
+        self.vapi.punt_socket_deregister(1111)
+        self.vapi.punt_socket_deregister(2222)
+        self.vapi.punt_socket_deregister(3333)
+        punts = self.vapi.punt_socket_dump(0)
+        self.assertEqual(len(punts), 0)
+
+    def test_punt_socket_traffic(self):
+        """ Punt socket traffic"""
+
+        nr_packets = 8
+        p = (Ether(src=self.pg0.remote_mac,
+                   dst=self.pg0.local_mac) /
+             IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
+             UDP(sport=9876, dport=1234) /
+             Raw('\xa5' * 100))
+
+        pkts = p * nr_packets
+
+        punts = self.vapi.punt_socket_dump(0)
+        self.assertEqual(len(punts), 0)
+
+        #
+        # expect ICMP - port unreachable for all packets
+        #
+        self.vapi.cli("clear trace")
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        rx = self.pg0.get_capture(nr_packets)
+        for p in rx:
+            self.assertEqual(int(p[IP].proto), 1)   # ICMP
+            self.assertEqual(int(p[ICMP].code), 3)  # unreachable
+
+        #
+        # configure a punt socket
+        #
+        self.socket_client_create(self.tempdir+"/socket_punt_1234")
+        self.vapi.punt_socket_register(1234, self.tempdir+"/socket_punt_1234")
+        punts = self.vapi.punt_socket_dump(0)
+        self.assertEqual(len(punts), 1)
+
+        #
+        # expect punt socket and no packets on pg0
+        #
+        self.vapi.cli("clear errors")
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg0.get_capture(0)
+        self.socket_client_close()
+
+        #
+        # remove punt socket. expect ICMP - port unreachable for all packets
+        #
+        self.vapi.punt_socket_deregister(1234)
+        punts = self.vapi.punt_socket_dump(0)
+        self.assertEqual(len(punts), 0)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        # FIXME - when punt socket deregister is implemented
+        # self.pg0.get_capture(nr_packets)
+
+
+class TestIP6PuntSocket(TestPuntSocket):
+    """ Punt Socket for IPv6"""
+
+    def setUp(self):
+        super(TestIP6PuntSocket, self).setUp()
+
+        self.create_pg_interfaces(range(2))
+
+        for i in self.pg_interfaces:
+            i.admin_up()
+            i.config_ip6()
+            i.resolve_ndp()
+
+    def tearDown(self):
+        super(TestIP6PuntSocket, self).tearDown()
+        for i in self.pg_interfaces:
+            i.unconfig_ip6()
+            i.admin_down()
+
+    def test_punt_socket_dump(self):
+        """ Punt socket registration """
+
+        punts = self.vapi.punt_socket_dump(0)
+        self.assertEqual(len(punts), 0)
+
+        #
+        # configure a punt socket
+        #
+        self.vapi.punt_socket_register(1111, self.tempdir+"/socket_punt_1111",
+                                       is_ip4=0)
+        self.vapi.punt_socket_register(2222, self.tempdir+"/socket_punt_2222",
+                                       is_ip4=0)
+        punts = self.vapi.punt_socket_dump(1)
+        self.assertEqual(len(punts), 2)
+        self.assertEqual(punts[0].punt.l4_port, 1111)
+        # self.assertEqual(punts[0].pathname, "/tmp/punt_socket_udp_1234")
+        self.assertEqual(punts[1].punt.l4_port, 2222)
+        # self.assertEqual(punts[1].pathname, "/tmp/punt_socket_udp_5678")
+
+        #
+        # deregister a punt socket
+        #
+        self.vapi.punt_socket_deregister(1111, is_ip4=0)
+        punts = self.vapi.punt_socket_dump(1)
+        self.assertEqual(len(punts), 1)
+
+        #
+        # configure a punt socket again
+        #
+        self.vapi.punt_socket_register(1111, self.tempdir+"/socket_punt_1111",
+                                       is_ip4=0)
+        punts = self.vapi.punt_socket_dump(1)
+        self.assertEqual(len(punts), 2)
+
+        #
+        # deregister all punt socket
+        #
+        self.vapi.punt_socket_deregister(1111, is_ip4=0)
+        self.vapi.punt_socket_deregister(2222, is_ip4=0)
+        self.vapi.punt_socket_deregister(3333, is_ip4=0)
+        punts = self.vapi.punt_socket_dump(1)
+        self.assertEqual(len(punts), 0)
+
+    def test_punt_socket_traffic(self):
+        """ Punt socket traffic"""
+
+        nr_packets = 2
+        p = (Ether(src=self.pg0.remote_mac,
+                   dst=self.pg0.local_mac) /
+             IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
+             inet6.UDP(sport=9876, dport=1234) /
+             Raw('\xa5' * 100))
+
+        pkts = p * nr_packets
+
+        punts = self.vapi.punt_socket_dump(1)
+        self.assertEqual(len(punts), 0)
+
+        #
+        # expect ICMPv6 - destination unreachable for all packets
+        #
+        self.vapi.cli("clear trace")
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        rx = self.pg0.get_capture(nr_packets)
+        for p in rx:
+            self.assertEqual(int(p[IPv6].nh), 58)                # ICMPv6
+            self.assertEqual(int(p[ICMPv6DestUnreach].code), 4)  # unreachable
+
+        #
+        # configure a punt socket
+        #
+        self.socket_client_create(self.tempdir+"/socket_punt_1234")
+        self.vapi.punt_socket_register(1234, self.tempdir+"/socket_punt_1234",
+                                       is_ip4=0)
+        punts = self.vapi.punt_socket_dump(1)
+        self.assertEqual(len(punts), 1)
+
+        #
+        # expect punt socket and no packets on pg0
+        #
+        self.vapi.cli("clear errors")
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg0.get_capture(0)
+        self.socket_client_close()
+
+        #
+        # remove punt socket. expect ICMP - dest. unreachable for all packets
+        #
+        self.vapi.punt_socket_deregister(1234, is_ip4=0)
+        punts = self.vapi.punt_socket_dump(1)
+        self.assertEqual(len(punts), 0)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        # FIXME - when punt socket deregister is implemented
+#        self.pg0.get_capture(nr_packets)
+
+
+if __name__ == '__main__':
+    unittest.main(testRunner=VppTestRunner)
index 8153e03..612a678 100644 (file)
@@ -3486,15 +3486,27 @@ class VppPapiProvider(object):
              'namespace_id': namespace_id,
              'namespace_id_len': len(namespace_id)})
 
-    def punt_socket_register(self, l4_port, pathname, header_version=1,
-                             is_ip4=1, l4_protocol=0x11):
-        """ Punt to socket """
+    def punt_socket_register(self, port, pathname, protocol=0x11,
+                             header_version=1, is_ip4=1):
+        """ Register punt socket """
         return self.api(self.papi.punt_socket_register,
-                        {'is_ip4': is_ip4,
-                         'l4_protocol': l4_protocol,
-                         'l4_port': l4_port,
-                         'pathname': pathname,
-                         'header_version': header_version})
+                        {'header_version': header_version,
+                         'punt': {'ipv': is_ip4,
+                                  'l4_protocol': protocol,
+                                  'l4_port': port},
+                         'pathname': pathname})
+
+    def punt_socket_deregister(self, port, protocol=0x11, is_ip4=1):
+        """ Unregister punt socket """
+        return self.api(self.papi.punt_socket_deregister,
+                        {'punt': {'ipv': is_ip4,
+                                  'l4_protocol': protocol,
+                                  'l4_port': port}})
+
+    def punt_socket_dump(self, is_ip6=1):
+        """ Dump punt socket"""
+        return self.api(self.papi.punt_socket_dump,
+                        {'is_ipv6': is_ip6})
 
     def ip_reassembly_set(self, timeout_ms, max_reassemblies,
                           expire_walk_interval_ms, is_ip6=0):