policer: output interface policer

Type: improvement

Change-Id: Ibc1b5059ed51c34334340534e9eb68121f556bce
Signed-off-by: Stanislav Zaikin <zstaseg@gmail.com>
diff --git a/src/vnet/policer/node_funcs.c b/src/vnet/policer/node_funcs.c
index 21b9393..5fa0c85 100644
--- a/src/vnet/policer/node_funcs.c
+++ b/src/vnet/policer/node_funcs.c
@@ -68,7 +68,7 @@
 
 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 @@
 	  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 @@
 
 	  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 @@
 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 @@
   .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_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;
diff --git a/src/vnet/policer/policer.api b/src/vnet/policer/policer.api
index a664ab0..f4bf938 100644
--- a/src/vnet/policer/policer.api
+++ b/src/vnet/policer/policer.api
@@ -52,6 +52,23 @@
   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
diff --git a/src/vnet/policer/policer.c b/src/vnet/policer/policer.c
index 9eb5641..0513563 100644
--- a/src/vnet/policer/policer.c
+++ b/src/vnet/policer/policer.c
@@ -166,7 +166,7 @@
 }
 
 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 @@
 
   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 @@
   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 @@
     }
   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 @@
   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,
 };
-/* *INDENT-ON* */
+
+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,
+};
 
 static clib_error_t *
 show_policer_command_fn (vlib_main_t * vm,
@@ -792,7 +813,10 @@
   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));
diff --git a/src/vnet/policer/policer.h b/src/vnet/policer/policer.h
index 2687064..f5b6c0d 100644
--- a/src/vnet/policer/policer.h
+++ b/src/vnet/policer/policer.h
@@ -39,7 +39,7 @@
   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 @@
   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 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 @@
 			       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__ */
 
diff --git a/src/vnet/policer/policer_api.c b/src/vnet/policer/policer_api.c
index 8c958e7..4f9baa0 100644
--- a/src/vnet/policer/policer_api.c
+++ b/src/vnet/policer/policer_api.c
@@ -120,7 +120,7 @@
   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;
@@ -128,6 +128,30 @@
 }
 
 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,
 		      u32 context)