policer: output interface policer 58/35958/10
authorStanislav Zaikin <zstaseg@gmail.com>
Tue, 5 Apr 2022 17:23:12 +0000 (19:23 +0200)
committerNeale Ranns <neale@graphiant.com>
Thu, 5 May 2022 00:19:12 +0000 (00:19 +0000)
Type: improvement

Change-Id: Ibc1b5059ed51c34334340534e9eb68121f556bce
Signed-off-by: Stanislav Zaikin <zstaseg@gmail.com>
src/vnet/policer/node_funcs.c
src/vnet/policer/policer.api
src/vnet/policer/policer.c
src/vnet/policer/policer.h
src/vnet/policer/policer_api.c
test/test_ipsec_tun_if_esp.py
test/test_policer_input.py
test/vpp_policer.py

index 21b9393..5fa0c85 100644 (file)
@@ -68,7 +68,7 @@ static char *vnet_policer_error_strings[] = {
 
 static inline uword
 vnet_policer_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
-                    vlib_frame_t *frame)
+                    vlib_frame_t *frame, vlib_dir_t dir)
 {
   u32 n_left_from, *from, *to_next;
   vnet_policer_next_t next_index;
@@ -120,11 +120,11 @@ vnet_policer_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
          b0 = vlib_get_buffer (vm, bi0);
          b1 = vlib_get_buffer (vm, bi1);
 
-         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
-         sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
+         sw_if_index0 = vnet_buffer (b0)->sw_if_index[dir];
+         sw_if_index1 = vnet_buffer (b1)->sw_if_index[dir];
 
-         pi0 = pm->policer_index_by_sw_if_index[sw_if_index0];
-         pi1 = pm->policer_index_by_sw_if_index[sw_if_index1];
+         pi0 = pm->policer_index_by_sw_if_index[dir][sw_if_index0];
+         pi1 = pm->policer_index_by_sw_if_index[dir][sw_if_index1];
 
          act0 = vnet_policer_police (vm, b0, pi0, time_in_policer_periods,
                                      POLICE_CONFORM /* no chaining */, true);
@@ -206,9 +206,8 @@ vnet_policer_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
 
          b0 = vlib_get_buffer (vm, bi0);
 
-         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
-
-         pi0 = pm->policer_index_by_sw_if_index[sw_if_index0];
+         sw_if_index0 = vnet_buffer (b0)->sw_if_index[dir];
+         pi0 = pm->policer_index_by_sw_if_index[dir][sw_if_index0];
 
          act0 = vnet_policer_police (vm, b0, pi0, time_in_policer_periods,
                                      POLICE_CONFORM /* no chaining */, true);
@@ -256,7 +255,7 @@ vnet_policer_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
 VLIB_NODE_FN (policer_input_node)
 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
 {
-  return vnet_policer_inline (vm, node, frame);
+  return vnet_policer_inline (vm, node, frame, VLIB_RX);
 }
 
 VLIB_REGISTER_NODE (policer_input_node) = {
@@ -279,12 +278,43 @@ VNET_FEATURE_INIT (policer_input_node, static) = {
   .runs_before = VNET_FEATURES ("ethernet-input"),
 };
 
+VLIB_NODE_FN (policer_output_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+  return vnet_policer_inline (vm, node, frame, VLIB_TX);
+}
+
+VLIB_REGISTER_NODE (policer_output_node) = {
+  .name = "policer-output",
+  .vector_size = sizeof (u32),
+  .format_trace = format_policer_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN(vnet_policer_error_strings),
+  .error_strings = vnet_policer_error_strings,
+  .n_next_nodes = VNET_POLICER_N_NEXT,
+  .next_nodes = {
+                [VNET_POLICER_NEXT_DROP] = "error-drop",
+                [VNET_POLICER_NEXT_HANDOFF] = "policer-output-handoff",
+                },
+};
+
+VNET_FEATURE_INIT (policer_output_node, static) = {
+  .arc_name = "ip4-output",
+  .node_name = "policer-output",
+};
+
+VNET_FEATURE_INIT (policer6_output_node, static) = {
+  .arc_name = "ip6-output",
+  .node_name = "policer-output",
+};
+
 static char *policer_input_handoff_error_strings[] = { "congestion drop" };
 
 VLIB_NODE_FN (policer_input_handoff_node)
 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
 {
-  return policer_handoff (vm, node, frame, vnet_policer_main.fq_index, ~0);
+  return policer_handoff (vm, node, frame, vnet_policer_main.fq_index[VLIB_RX],
+                         ~0);
 }
 
 VLIB_REGISTER_NODE (policer_input_handoff_node) = {
@@ -301,6 +331,26 @@ VLIB_REGISTER_NODE (policer_input_handoff_node) = {
   },
 };
 
+VLIB_NODE_FN (policer_output_handoff_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+  return policer_handoff (vm, node, frame, vnet_policer_main.fq_index[VLIB_TX],
+                         ~0);
+}
+
+VLIB_REGISTER_NODE (policer_output_handoff_node) = {
+  .name = "policer-output-handoff",
+  .vector_size = sizeof (u32),
+  .format_trace = format_policer_handoff_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN(policer_input_handoff_error_strings),
+  .error_strings = policer_input_handoff_error_strings,
+
+  .n_next_nodes = 1,
+  .next_nodes = {
+    [0] = "error-drop",
+  },
+};
 typedef struct
 {
   u32 sw_if_index;
index a664ab0..f4bf938 100644 (file)
@@ -52,6 +52,23 @@ autoreply define policer_input
   bool apply;
 };
 
+/** \brief policer output: Apply policer as an output feature.
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param name - policer name
+    @param sw_if_index - interface to apply the policer
+    @param apply - Apply/remove
+*/
+autoreply define policer_output
+{
+  u32 client_index;
+  u32 context;
+
+  string name[64];
+  vl_api_interface_index_t sw_if_index;
+  bool apply;
+};
+
 /** \brief Add/del policer
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
index 9eb5641..0513563 100644 (file)
@@ -166,7 +166,7 @@ policer_bind_worker (u8 *name, u32 worker, bool bind)
 }
 
 int
-policer_input (u8 *name, u32 sw_if_index, bool apply)
+policer_input (u8 *name, u32 sw_if_index, vlib_dir_t dir, bool apply)
 {
   vnet_policer_main_t *pm = &vnet_policer_main;
   policer_t *policer;
@@ -184,16 +184,26 @@ policer_input (u8 *name, u32 sw_if_index, bool apply)
 
   if (apply)
     {
-      vec_validate (pm->policer_index_by_sw_if_index, sw_if_index);
-      pm->policer_index_by_sw_if_index[sw_if_index] = policer_index;
+      vec_validate (pm->policer_index_by_sw_if_index[dir], sw_if_index);
+      pm->policer_index_by_sw_if_index[dir][sw_if_index] = policer_index;
     }
   else
     {
-      pm->policer_index_by_sw_if_index[sw_if_index] = ~0;
+      pm->policer_index_by_sw_if_index[dir][sw_if_index] = ~0;
     }
 
-  vnet_feature_enable_disable ("device-input", "policer-input", sw_if_index,
-                              apply, 0, 0);
+  if (dir == VLIB_RX)
+    {
+      vnet_feature_enable_disable ("device-input", "policer-input",
+                                  sw_if_index, apply, 0, 0);
+    }
+  else
+    {
+      vnet_feature_enable_disable ("ip4-output", "policer-output", sw_if_index,
+                                  apply, 0, 0);
+      vnet_feature_enable_disable ("ip6-output", "policer-output", sw_if_index,
+                                  apply, 0, 0);
+    }
   return 0;
 }
 
@@ -637,6 +647,7 @@ policer_input_command_fn (vlib_main_t *vm, unformat_input_t *input,
   u8 apply, *name = 0;
   u32 sw_if_index;
   int rv;
+  vlib_dir_t dir = cmd->function_arg;
 
   apply = 1;
   sw_if_index = ~0;
@@ -669,7 +680,7 @@ policer_input_command_fn (vlib_main_t *vm, unformat_input_t *input,
     }
   else
     {
-      rv = policer_input (name, sw_if_index, apply);
+      rv = policer_input (name, sw_if_index, dir, apply);
 
       if (rv)
        error = clib_error_return (0, "failed: `%d'", rv);
@@ -681,33 +692,43 @@ done:
   return error;
 }
 
-/* *INDENT-OFF* */
 VLIB_CLI_COMMAND (configure_policer_command, static) = {
   .path = "configure policer",
   .short_help = "configure policer name <name> <params> ",
   .function = policer_add_command_fn,
 };
+
 VLIB_CLI_COMMAND (policer_add_command, static) = {
   .path = "policer add",
   .short_help = "policer name <name> <params> ",
   .function = policer_add_command_fn,
 };
+
 VLIB_CLI_COMMAND (policer_del_command, static) = {
   .path = "policer del",
   .short_help = "policer del name <name> ",
   .function = policer_del_command_fn,
 };
+
 VLIB_CLI_COMMAND (policer_bind_command, static) = {
   .path = "policer bind",
   .short_help = "policer bind [unbind] name <name> <worker>",
   .function = policer_bind_command_fn,
 };
+
 VLIB_CLI_COMMAND (policer_input_command, static) = {
   .path = "policer input",
   .short_help = "policer input [unapply] name <name> <interfac>",
   .function = policer_input_command_fn,
+  .function_arg = VLIB_RX,
+};
+
+VLIB_CLI_COMMAND (policer_output_command, static) = {
+  .path = "policer output",
+  .short_help = "policer output [unapply] name <name> <interfac>",
+  .function = policer_input_command_fn,
+  .function_arg = VLIB_TX,
 };
-/* *INDENT-ON* */
 
 static clib_error_t *
 show_policer_command_fn (vlib_main_t * vm,
@@ -792,7 +813,10 @@ policer_init (vlib_main_t * vm)
   pm->vlib_main = vm;
   pm->vnet_main = vnet_get_main ();
   pm->log_class = vlib_log_register_class ("policer", 0);
-  pm->fq_index = vlib_frame_queue_main_init (policer_input_node.index, 0);
+  pm->fq_index[VLIB_RX] =
+    vlib_frame_queue_main_init (policer_input_node.index, 0);
+  pm->fq_index[VLIB_TX] =
+    vlib_frame_queue_main_init (policer_output_node.index, 0);
 
   pm->policer_config_by_name = hash_create_string (0, sizeof (uword));
   pm->policer_index_by_name = hash_create_string (0, sizeof (uword));
index 2687064..f5b6c0d 100644 (file)
@@ -39,7 +39,7 @@ typedef struct
   uword *policer_index_by_name;
 
   /* Policer by sw_if_index vector */
-  u32 *policer_index_by_sw_if_index;
+  u32 *policer_index_by_sw_if_index[VLIB_N_RX_TX];
 
   /* convenience */
   vlib_main_t *vlib_main;
@@ -48,7 +48,7 @@ typedef struct
   vlib_log_class_t log_class;
 
   /* frame queue for thread handoff */
-  u32 fq_index;
+  u32 fq_index[VLIB_N_RX_TX];
 
   u16 msg_id_base;
 } vnet_policer_main_t;
@@ -58,6 +58,7 @@ extern vnet_policer_main_t vnet_policer_main;
 extern vlib_combined_counter_main_t policer_counters[];
 
 extern vlib_node_registration_t policer_input_node;
+extern vlib_node_registration_t policer_output_node;
 
 typedef enum
 {
@@ -71,7 +72,7 @@ clib_error_t *policer_add_del (vlib_main_t *vm, u8 *name,
                               qos_pol_cfg_params_st *cfg, u32 *policer_index,
                               u8 is_add);
 int policer_bind_worker (u8 *name, u32 worker, bool bind);
-int policer_input (u8 *name, u32 sw_if_index, bool apply);
+int policer_input (u8 *name, u32 sw_if_index, vlib_dir_t dir, bool apply);
 
 #endif /* __included_policer_h__ */
 
index 8c958e7..4f9baa0 100644 (file)
@@ -120,13 +120,37 @@ vl_api_policer_input_t_handler (vl_api_policer_input_t *mp)
   sw_if_index = ntohl (mp->sw_if_index);
   apply = mp->apply;
 
-  rv = policer_input (name, sw_if_index, apply);
+  rv = policer_input (name, sw_if_index, VLIB_RX, apply);
   vec_free (name);
 
   BAD_SW_IF_INDEX_LABEL;
   REPLY_MACRO (VL_API_POLICER_INPUT_REPLY);
 }
 
+static void
+vl_api_policer_output_t_handler (vl_api_policer_input_t *mp)
+{
+  vl_api_policer_bind_reply_t *rmp;
+  u8 *name;
+  u32 sw_if_index;
+  u8 apply;
+  int rv;
+
+  VALIDATE_SW_IF_INDEX (mp);
+
+  name = format (0, "%s", mp->name);
+  vec_terminate_c_string (name);
+
+  sw_if_index = ntohl (mp->sw_if_index);
+  apply = mp->apply;
+
+  rv = policer_input (name, sw_if_index, VLIB_TX, apply);
+  vec_free (name);
+
+  BAD_SW_IF_INDEX_LABEL;
+  REPLY_MACRO (VL_API_POLICER_OUTPUT_REPLY);
+}
+
 static void
 send_policer_details (u8 *name, qos_pol_cfg_params_st *config,
                      policer_t *templ, vl_api_registration_t *reg,
index 8b6f619..14c9b3e 100644 (file)
@@ -25,7 +25,7 @@ from util import ppp
 from vpp_papi import VppEnum
 from vpp_papi_provider import CliFailedCommandError
 from vpp_acl import AclRule, VppAcl, VppAclInterface
-from vpp_policer import PolicerAction, VppPolicer
+from vpp_policer import PolicerAction, VppPolicer, Dir
 
 
 def config_tun_params(p, encryption_type, tun_if, src=None, dst=None):
@@ -513,7 +513,7 @@ class TestIpsec6TunIfEspHandoff(TemplateIpsec6TunIfEsp,
         policer.add_vpp_config()
 
         # Start policing on tun
-        policer.apply_vpp_config(p.tun_if.sw_if_index, True)
+        policer.apply_vpp_config(p.tun_if.sw_if_index, Dir.RX, True)
 
         for pol_bind in [1, 0]:
             policer.bind_vpp_config(pol_bind, True)
@@ -557,7 +557,7 @@ class TestIpsec6TunIfEspHandoff(TemplateIpsec6TunIfEsp,
                                  stats1['conform_packets'] +
                                  stats1['violate_packets'])
 
-        policer.apply_vpp_config(p.tun_if.sw_if_index, False)
+        policer.apply_vpp_config(p.tun_if.sw_if_index, Dir.RX, False)
         policer.remove_vpp_config()
 
 
@@ -585,7 +585,7 @@ class TestIpsec4TunIfEspHandoff(TemplateIpsec4TunIfEsp,
         policer.add_vpp_config()
 
         # Start policing on tun
-        policer.apply_vpp_config(p.tun_if.sw_if_index, True)
+        policer.apply_vpp_config(p.tun_if.sw_if_index, Dir.RX, True)
 
         for pol_bind in [1, 0]:
             policer.bind_vpp_config(pol_bind, True)
@@ -629,7 +629,7 @@ class TestIpsec4TunIfEspHandoff(TemplateIpsec4TunIfEsp,
                                  stats1['conform_packets'] +
                                  stats1['violate_packets'])
 
-        policer.apply_vpp_config(p.tun_if.sw_if_index, False)
+        policer.apply_vpp_config(p.tun_if.sw_if_index, Dir.RX, False)
         policer.remove_vpp_config()
 
 
@@ -2726,7 +2726,7 @@ class TestIpsecItf4(TemplateIpsec,
         policer.add_vpp_config()
 
         # Start policing on tun
-        policer.apply_vpp_config(p.tun_if.sw_if_index, True)
+        policer.apply_vpp_config(p.tun_if.sw_if_index, Dir.RX, True)
 
         self.verify_tun_44(p, count=n_pkts)
         self.assertEqual(p.tun_if.get_rx_stats(), n_pkts)
@@ -2740,7 +2740,7 @@ class TestIpsecItf4(TemplateIpsec,
         self.assertGreater(stats['violate_packets'], 0)
 
         # Stop policing on tun
-        policer.apply_vpp_config(p.tun_if.sw_if_index, False)
+        policer.apply_vpp_config(p.tun_if.sw_if_index, Dir.RX, False)
         self.verify_tun_44(p, count=n_pkts)
 
         # No new policer stats
@@ -3017,7 +3017,7 @@ class TestIpsecItf6(TemplateIpsec,
         policer.add_vpp_config()
 
         # Start policing on tun
-        policer.apply_vpp_config(p.tun_if.sw_if_index, True)
+        policer.apply_vpp_config(p.tun_if.sw_if_index, Dir.RX, True)
 
         self.verify_tun_66(p, count=n_pkts)
         self.assertEqual(p.tun_if.get_rx_stats(), n_pkts)
@@ -3031,7 +3031,7 @@ class TestIpsecItf6(TemplateIpsec,
         self.assertGreater(stats['violate_packets'], 0)
 
         # Stop policing on tun
-        policer.apply_vpp_config(p.tun_if.sw_if_index, False)
+        policer.apply_vpp_config(p.tun_if.sw_if_index, Dir.RX, False)
         self.verify_tun_66(p, count=n_pkts)
 
         # No new policer stats
index c95f664..9a4266c 100644 (file)
@@ -8,13 +8,13 @@ from scapy.layers.l2 import Ether
 from scapy.packet import Raw
 from framework import VppTestCase, VppTestRunner
 from vpp_papi import VppEnum
-from vpp_policer import VppPolicer, PolicerAction
+from vpp_policer import VppPolicer, PolicerAction, Dir
 
 NUM_PKTS = 67
 
 
 class TestPolicerInput(VppTestCase):
-    """ Policer on an input interface """
+    """ Policer on an interface """
     vpp_worker_count = 2
 
     def setUp(self):
@@ -38,8 +38,7 @@ class TestPolicerInput(VppTestCase):
             i.admin_down()
         super(TestPolicerInput, self).tearDown()
 
-    def test_policer_input(self):
-        """ Input Policing """
+    def policer_interface_test(self, dir: Dir):
         pkts = self.pkt * NUM_PKTS
 
         action_tx = PolicerAction(
@@ -51,8 +50,12 @@ class TestPolicerInput(VppTestCase):
                              violate_action=action_tx)
         policer.add_vpp_config()
 
+        sw_if_index = (self.pg0.sw_if_index
+                       if dir == Dir.RX
+                       else self.pg1.sw_if_index)
+
         # Start policing on pg0
-        policer.apply_vpp_config(self.pg0.sw_if_index, True)
+        policer.apply_vpp_config(sw_if_index, dir, True)
 
         rx = self.send_and_expect(self.pg0, pkts, self.pg1, worker=0)
         stats = policer.get_stats()
@@ -63,7 +66,7 @@ class TestPolicerInput(VppTestCase):
         self.assertGreater(stats['violate_packets'], 0)
 
         # Stop policing on pg0
-        policer.apply_vpp_config(self.pg0.sw_if_index, False)
+        policer.apply_vpp_config(sw_if_index, dir, False)
 
         rx = self.send_and_expect(self.pg0, pkts, self.pg1, worker=0)
 
@@ -74,8 +77,15 @@ class TestPolicerInput(VppTestCase):
 
         policer.remove_vpp_config()
 
-    def test_policer_handoff(self):
-        """ Worker thread handoff """
+    def test_policer_input(self):
+        """ Input Policing """
+        self.policer_interface_test(Dir.RX)
+
+    def test_policer_output(self):
+        """ Output Policing """
+        self.policer_interface_test(Dir.TX)
+
+    def policer_handoff_test(self, dir: Dir):
         pkts = self.pkt * NUM_PKTS
 
         action_tx = PolicerAction(
@@ -87,11 +97,15 @@ class TestPolicerInput(VppTestCase):
                              violate_action=action_tx)
         policer.add_vpp_config()
 
+        sw_if_index = (self.pg0.sw_if_index
+                       if dir == Dir.RX
+                       else self.pg1.sw_if_index)
+
         # Bind the policer to worker 1
         policer.bind_vpp_config(1, True)
 
         # Start policing on pg0
-        policer.apply_vpp_config(self.pg0.sw_if_index, True)
+        policer.apply_vpp_config(sw_if_index, dir, True)
 
         for worker in [0, 1]:
             self.send_and_expect(self.pg0, pkts, self.pg1, worker=worker)
@@ -138,9 +152,18 @@ class TestPolicerInput(VppTestCase):
                          stats['violate_packets'])
 
         # Stop policing on pg0
-        policer.apply_vpp_config(self.pg0.sw_if_index, False)
+        policer.apply_vpp_config(sw_if_index, dir, False)
 
         policer.remove_vpp_config()
 
+    def test_policer_handoff_input(self):
+        """ Worker thread handoff policer input"""
+        self.policer_handoff_test(Dir.RX)
+
+    def test_policer_handoff_output(self):
+        """ Worker thread handoff policer output"""
+        self.policer_handoff_test(Dir.TX)
+
+
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
index 2c47eed..0f3b073 100644 (file)
@@ -1,5 +1,11 @@
 from vpp_object import VppObject
 from vpp_ip import INVALID_INDEX
+from enum import Enum
+
+
+class Dir(Enum):
+    RX = 0
+    TX = 1
 
 
 class PolicerAction():
@@ -61,9 +67,13 @@ class VppPolicer(VppObject):
         self._test.vapi.policer_bind(name=self.name, worker_index=worker,
                                      bind_enable=bind)
 
-    def apply_vpp_config(self, if_index, apply):
-        self._test.vapi.policer_input(name=self.name, sw_if_index=if_index,
-                                      apply=apply)
+    def apply_vpp_config(self, if_index, dir: Dir, apply):
+        if dir == Dir.RX:
+            self._test.vapi.policer_input(
+                name=self.name, sw_if_index=if_index, apply=apply)
+        else:
+            self._test.vapi.policer_output(
+                name=self.name, sw_if_index=if_index, apply=apply)
 
     def query_vpp_config(self):
         dump = self._test.vapi.policer_dump(