Add an ability to punt all unknown UDP traffic to the host
authorAlexander Popovsky (apopovsk) <[email protected]>
Tue, 15 Nov 2016 23:36:23 +0000 (15:36 -0800)
committerJohn Lo <[email protected]>
Wed, 16 Nov 2016 07:24:26 +0000 (07:24 +0000)
By default, VPP replies with ICMP error: port unreachable when receives
an ‘unknown’ UDP (destination port with no registered listener) packet.

An existing punt() API is extended to accept ALL (~0) as a L4 port
number and if used redirects all ‘unknown’ UDP packets to the host.

New ‘all’ option is added to the “set punt udp” CLI as well.

Change-Id: I444fc5e32ffa3f0f085bb17708bf32b883ba09df
Signed-off-by: Alexander Popovsky (apopovsk) <[email protected]>
vnet/vnet/ip/punt.c
vnet/vnet/ip/udp.h
vnet/vnet/ip/udp_error.def
vnet/vnet/ip/udp_local.c

index 188d03a..30d3deb 100644 (file)
@@ -219,38 +219,39 @@ clib_error_t *
 vnet_punt_add_del (vlib_main_t * vm, u8 ipv, u8 protocol, u16 port,
                   int is_add)
 {
-  /* For now we only support adding specific UDP port punt */
-  {
-    if (!is_add)
-      return clib_error_return (0, "punt delete is not supported yet");
+  /* For now we only support UDP punt */
+  if (protocol != IP_PROTOCOL_UDP)
+    return clib_error_return (0,
+                             "only UDP protocol (%d) is supported, got %d",
+                             IP_PROTOCOL_UDP, protocol);
 
-    if (protocol != IP_PROTOCOL_UDP)
-      return clib_error_return (0,
-                               "only UDP protocol (%d) is supported, got %d",
-                               IP_PROTOCOL_UDP, protocol);
+  if (ipv != (u8) ~ 0 && ipv != 4 && ipv != 6)
+    return clib_error_return (0, "IP version must be 4 or 6, got %d", ipv);
 
-    if (port == (u16) ~ 0)
-      return clib_error_return (0, "TCP/UDP port must be specified");
-  }
+  if (port == (u16) ~ 0)
+    {
+      if (ipv == 4 || ipv == (u8) ~ 0)
+       udp_punt_unknown (vm, 1, is_add);
+
+      if (ipv == 6 || ipv == (u8) ~ 0)
+       udp_punt_unknown (vm, 0, is_add);
 
-  if (ipv != (u8) ~ 0)
+      return 0;
+    }
+
+  else if (is_add)
     {
-      if (ipv == 4)
+      if (ipv == 4 || ipv == (u8) ~ 0)
        udp_register_dst_port (vm, port, udp4_punt_node.index, 1);
-      else if (ipv == 6)
+
+      if (ipv == 6 || ipv == (u8) ~ 0)
        udp_register_dst_port (vm, port, udp6_punt_node.index, 0);
-      else
-       return clib_error_return (0, "IP version must be 4 or 6, got %d",
-                                 ipv);
-    }
-  else
-    {
-      udp_register_dst_port (vm, port, udp4_punt_node.index, 1);
-      udp_register_dst_port (vm, port, udp6_punt_node.index, 0);
-    }
 
-  return 0;
+      return 0;
+    }
 
+  else
+    return clib_error_return (0, "punt delete is not supported yet");
 }
 
 static clib_error_t *
@@ -265,6 +266,13 @@ udp_punt_cli (vlib_main_t * vm,
     {
       if (unformat (input, "del"))
        is_add = 0;
+      if (unformat (input, "all"))
+       {
+         /* punt both IPv6 and IPv4 when used in CLI */
+         error = vnet_punt_add_del (vm, ~0, IP_PROTOCOL_UDP, ~0, is_add);
+         if (error)
+           clib_error_report (error);
+       }
       else if (unformat (input, "%d", &udp_port))
        {
          /* punt both IPv6 and IPv4 when used in CLI */
@@ -284,7 +292,6 @@ udp_punt_cli (vlib_main_t * vm,
  *
  * @em Note
  * - UDP is the only protocol supported in the current implementation
- * - When requesting UDP punt port number(s) must be specified
  * - All TCP traffic is currently punted to the host by default
  *
  * @cliexpar
@@ -292,12 +299,17 @@ udp_punt_cli (vlib_main_t * vm,
  * Example of how to request NTP traffic to be punted
  * @cliexcmd{set punt udp 125}
  *
+ * Example of how to request all 'unknown' UDP traffic to be punted
+ * @cliexcmd{set punt udp all}
+ *
+ * Example of how to stop all 'unknown' UDP traffic to be punted
+ * @cliexcmd{set punt udp del all}
  * @endparblock
 ?*/
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (punt_udp_command, static) = {
   .path = "set punt udp",
-  .short_help = "set punt udp [del] port-num1 [port-num2 ...]",
+  .short_help = "set punt udp [del] <all | port-num1 [port-num2 ...]>",
   .function = udp_punt_cli,
 };
 /* *INDENT-ON* */
index 1845fa7..4de30f1 100644 (file)
@@ -114,6 +114,8 @@ void udp_register_dst_port (vlib_main_t * vm,
                             udp_dst_port_t dst_port,
                             u32 node_index, u8 is_ip4);
 
+void udp_punt_unknown(vlib_main_t * vm, u8 is_ip4, u8 is_add);
+
 always_inline void
 ip_udp_fixup_one (vlib_main_t * vm,
                   vlib_buffer_t * b0,
index 311d206..bfdae0a 100644 (file)
@@ -18,3 +18,4 @@
 udp_error (NONE, "no error")
 udp_error (NO_LISTENER, "no listener for dst port")
 udp_error (LENGTH_ERROR, "UDP packets with length errors")
+udp_error (PUNT, "no listener punt")
index 7676630..e4f64a5 100644 (file)
@@ -59,6 +59,7 @@ typedef struct {
   /* Sparse vector mapping udp dst_port in network byte order
      to next index. */
   u16 * next_by_dst_port;
+  u8 punt_unknown;
 } udp_input_runtime_t;
 
 vlib_node_registration_t udp4_input_node;
@@ -75,6 +76,7 @@ udp46_input_inline (vlib_main_t * vm,
     : (void *) vlib_node_get_runtime_data (vm, udp6_input_node.index);
   __attribute__((unused)) u32 n_left_from, next_index, * from, * to_next;
   word n_no_listener = 0;
+  u8 punt_unknown = rt->punt_unknown;
 
   from = vlib_frame_vector_args (from_frame);
   n_left_from = from_frame->n_vectors;
@@ -185,19 +187,25 @@ udp46_input_inline (vlib_main_t * vm,
               // ip packet header
               vlib_buffer_advance (b0, - (word)advance0);
 
-              if (is_ip4)
+              if (PREDICT_FALSE(punt_unknown))
+               {
+                  b0->error = node->errors[UDP_ERROR_PUNT];
+                 next0 = UDP_INPUT_NEXT_PUNT;
+               }
+              else if (is_ip4)
                 {
                   icmp4_error_set_vnet_buffer(b0, ICMP4_destination_unreachable,
                         ICMP4_destination_unreachable_port_unreachable, 0);
                   next0 = UDP_INPUT_NEXT_ICMP4_ERROR;
+                  n_no_listener ++;
                 }
               else
                 {
                   icmp6_error_set_vnet_buffer(b0, ICMP6_destination_unreachable,
                         ICMP6_destination_unreachable_port_unreachable, 0);
                   next0 = UDP_INPUT_NEXT_ICMP6_ERROR;
+                  n_no_listener ++;
                 }
-                n_no_listener ++;
             }
           else
             {
@@ -212,19 +220,25 @@ udp46_input_inline (vlib_main_t * vm,
               // ip packet header
               vlib_buffer_advance (b1, - (word)advance1);
 
-              if (is_ip4)
+              if (PREDICT_FALSE(punt_unknown))
+               {
+                  b1->error = node->errors[UDP_ERROR_PUNT];
+                 next1 = UDP_INPUT_NEXT_PUNT;
+               }
+              else if (is_ip4)
                 {
                   icmp4_error_set_vnet_buffer(b1, ICMP4_destination_unreachable,
                         ICMP4_destination_unreachable_port_unreachable, 0);
                   next1 = UDP_INPUT_NEXT_ICMP4_ERROR;
+                 n_no_listener ++;
                 }
               else
                 {
                   icmp6_error_set_vnet_buffer(b1, ICMP6_destination_unreachable,
                         ICMP6_destination_unreachable_port_unreachable, 0);
                   next1 = UDP_INPUT_NEXT_ICMP6_ERROR;
+                 n_no_listener ++;
                 }
-                n_no_listener ++;
             }
           else
             {
@@ -309,19 +323,25 @@ udp46_input_inline (vlib_main_t * vm,
                   // ip packet header
                   vlib_buffer_advance (b0, - (word)advance0);
 
-                  if (is_ip4)
+                  if (PREDICT_FALSE(punt_unknown))
+                   {
+                      b0->error = node->errors[UDP_ERROR_PUNT];
+                      next0 = UDP_INPUT_NEXT_PUNT;
+                   }
+                  else if (is_ip4)
                     {
                       icmp4_error_set_vnet_buffer(b0, ICMP4_destination_unreachable,
                             ICMP4_destination_unreachable_port_unreachable, 0);
                       next0 = UDP_INPUT_NEXT_ICMP4_ERROR;
+                      n_no_listener ++;
                     }
                   else
                     {
                       icmp6_error_set_vnet_buffer(b0, ICMP6_destination_unreachable,
                             ICMP6_destination_unreachable_port_unreachable, 0);
                       next0 = UDP_INPUT_NEXT_ICMP6_ERROR;
+                      n_no_listener ++;
                     }
-                    n_no_listener ++;
                 }
               else
                 {
@@ -492,6 +512,23 @@ udp_register_dst_port (vlib_main_t * vm,
   n[0] = pi->next_index;
 }
 
+void
+udp_punt_unknown(vlib_main_t * vm, u8 is_ip4, u8 is_add)
+{
+  udp_input_runtime_t * rt;
+
+  {
+    clib_error_t * error = vlib_call_init_function (vm, udp_local_init);
+    if (error)
+      clib_error_report (error);
+  }
+
+  rt = vlib_node_get_runtime_data 
+    (vm, is_ip4 ? udp4_input_node.index: udp6_input_node.index);
+
+  rt->punt_unknown = is_add;
+}
+
 /* Parse a UDP header. */
 uword unformat_udp_header (unformat_input_t * input, va_list * args)
 {
@@ -560,6 +597,8 @@ clib_error_t * udp_local_init (vlib_main_t * vm)
     (/* elt bytes */ sizeof (rt->next_by_dst_port[0]),
      /* bits in index */ BITS (((udp_header_t *) 0)->dst_port));
 
+  rt->punt_unknown = 0;
+
 #define _(n,s) add_dst_port (um, UDP_DST_PORT_##s, #s, 1 /* is_ip4 */);
   foreach_udp4_dst_port
 #undef _
@@ -570,6 +609,8 @@ clib_error_t * udp_local_init (vlib_main_t * vm)
     (/* elt bytes */ sizeof (rt->next_by_dst_port[0]),
      /* bits in index */ BITS (((udp_header_t *) 0)->dst_port));
 
+  rt->punt_unknown = 0;
+
 #define _(n,s) add_dst_port (um, UDP_DST_PORT_##s, #s, 0 /* is_ip4 */);
   foreach_udp6_dst_port
 #undef _