ipfix: add classification nodes for flow statistics (VPP-204) 62/2262/13
authorJuraj Sloboda <jsloboda@cisco.com>
Mon, 8 Aug 2016 06:45:24 +0000 (23:45 -0700)
committerDamjan Marion <dmarion.lists@gmail.com>
Mon, 10 Oct 2016 20:51:28 +0000 (20:51 +0000)
In order to have meaningfull IPFIX implementation we should be able
to classify all packets flowing through vpp. But existing IPv4 and IPv6
classifier nodes are called only if destination IP address is local
to vpp. This commit adds new IPv4 and IPv6 classifier nodes that should
be used for collecting flow statistics.

Change-Id: I60e60105663ba15b5200862a23bb817047fe4d1a
Signed-off-by: Juraj Sloboda <jsloboda@cisco.com>
13 files changed:
vnet/Makefile.am
vnet/vnet/classify/flow_classify.c [new file with mode: 0644]
vnet/vnet/classify/flow_classify.h [new file with mode: 0644]
vnet/vnet/classify/flow_classify_node.c [new file with mode: 0644]
vnet/vnet/ip/ip4.h
vnet/vnet/ip/ip4_forward.c
vnet/vnet/ip/ip6.h
vnet/vnet/ip/ip6_forward.c
vnet/vnet/ip/ip_init.c
vpp-api-test/vat/api_format.c
vpp/vpp-api/api.c
vpp/vpp-api/custom_dump.c
vpp/vpp-api/vpe.api

index c0ae70d..f130d15 100644 (file)
@@ -249,12 +249,15 @@ libvnet_la_SOURCES +=                             \
   vnet/classify/ip_classify.c                  \
   vnet/classify/input_acl.c                    \
   vnet/classify/policer_classify.c             \
+  vnet/classify/flow_classify.c                 \
+  vnet/classify/flow_classify_node.c            \
   vnet/classify/vnet_classify.h
 
 nobase_include_HEADERS +=                      \
   vnet/classify/vnet_classify.h                        \
   vnet/classify/input_acl.h                     \
-  vnet/classify/policer_classify.h
+  vnet/classify/policer_classify.h              \
+  vnet/classify/flow_classify.h
 
 ########################################
 # Layer 3 protocols go here
diff --git a/vnet/vnet/classify/flow_classify.c b/vnet/vnet/classify/flow_classify.c
new file mode 100644 (file)
index 0000000..3269994
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <vnet/classify/flow_classify.h>
+
+static void
+vnet_flow_classify_feature_enable (vlib_main_t * vnm,
+                                   flow_classify_main_t * fcm,
+                                   u32 sw_if_index,
+                                   flow_classify_table_id_t tid,
+                                   int feature_enable)
+{
+  ip_lookup_main_t * lm;
+  ip_config_main_t * ifcm;
+  u32 ftype;
+  u32 ci;
+
+  if (tid == FLOW_CLASSIFY_TABLE_IP4)
+  {
+    lm = &ip4_main.lookup_main;
+    ftype = ip4_main.ip4_unicast_rx_feature_flow_classify;
+  }
+  else
+  {
+    lm = &ip6_main.lookup_main;
+    ftype = ip6_main.ip6_unicast_rx_feature_flow_classify;
+  }
+
+  ifcm = &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
+
+  ci = ifcm->config_index_by_sw_if_index[sw_if_index];
+  ci = (feature_enable ? vnet_config_add_feature : vnet_config_del_feature)
+  (vnm, &ifcm->config_main, ci, ftype, 0, 0);
+
+  ifcm->config_index_by_sw_if_index[sw_if_index] = ci;
+  fcm->vnet_config_main[tid] = &ifcm->config_main;
+}
+
+int vnet_set_flow_classify_intfc (vlib_main_t * vm, u32 sw_if_index,
+                                  u32 ip4_table_index, u32 ip6_table_index,
+                                  u32 is_add)
+{
+  flow_classify_main_t * fcm = &flow_classify_main;
+  vnet_classify_main_t * vcm = fcm->vnet_classify_main;
+  u32 pct[FLOW_CLASSIFY_N_TABLES] = {ip4_table_index, ip6_table_index};
+  u32 ti;
+
+  /* Assume that we've validated sw_if_index in the API layer */
+
+  for (ti = 0; ti < FLOW_CLASSIFY_N_TABLES; ti++)
+    {
+      if (pct[ti] == ~0)
+        continue;
+
+      if (pool_is_free_index (vcm->tables, pct[ti]))
+        return VNET_API_ERROR_NO_SUCH_TABLE;
+
+      vec_validate_init_empty
+        (fcm->classify_table_index_by_sw_if_index[ti], sw_if_index, ~0);
+
+      /* Reject any DEL operation with wrong sw_if_index */
+      if (!is_add &&
+          (pct[ti] != fcm->classify_table_index_by_sw_if_index[ti][sw_if_index]))
+        {
+          clib_warning ("Non-existent intf_idx=%d with table_index=%d for delete",
+                        sw_if_index, pct[ti]);
+          return VNET_API_ERROR_NO_SUCH_TABLE;
+        }
+
+      /* Return ok on ADD operaton if feature is already enabled */
+      if (is_add &&
+          fcm->classify_table_index_by_sw_if_index[ti][sw_if_index] != ~0)
+          return 0;
+
+      vnet_flow_classify_feature_enable (vm, fcm, sw_if_index, ti, is_add);
+
+      if (is_add)
+        fcm->classify_table_index_by_sw_if_index[ti][sw_if_index] = pct[ti];
+      else
+        fcm->classify_table_index_by_sw_if_index[ti][sw_if_index] = ~0;
+    }
+
+
+  return 0;
+}
+
+static clib_error_t *
+set_flow_classify_command_fn (vlib_main_t * vm,
+                              unformat_input_t * input,
+                              vlib_cli_command_t * cmd)
+{
+  vnet_main_t * vnm = vnet_get_main();
+  u32 sw_if_index = ~0;
+  u32 ip4_table_index = ~0;
+  u32 ip6_table_index = ~0;
+  u32 is_add = 1;
+  u32 idx_cnt = 0;
+  int rv;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "interface %U", unformat_vnet_sw_interface,
+                    vnm, &sw_if_index))
+        ;
+      else if (unformat (input, "ip4-table %d", &ip4_table_index))
+        idx_cnt++;
+      else if (unformat (input, "ip6-table %d", &ip6_table_index))
+        idx_cnt++;
+      else if (unformat (input, "del"))
+        is_add = 0;
+      else
+        break;
+    }
+
+  if (sw_if_index == ~0)
+    return clib_error_return (0, "Interface must be specified.");
+
+  if (!idx_cnt)
+    return clib_error_return (0, "Table index should be specified.");
+
+  if (idx_cnt > 1)
+    return clib_error_return (0, "Only one table index per API is allowed.");
+
+  rv = vnet_set_flow_classify_intfc(vm, sw_if_index, ip4_table_index,
+                                       ip6_table_index, is_add);
+
+  switch (rv)
+    {
+    case 0:
+      break;
+
+    case VNET_API_ERROR_NO_MATCHING_INTERFACE:
+      return clib_error_return (0, "No such interface");
+
+    case VNET_API_ERROR_NO_SUCH_ENTRY:
+      return clib_error_return (0, "No such classifier table");
+    }
+  return 0;
+}
+
+VLIB_CLI_COMMAND (set_input_acl_command, static) = {
+    .path = "set flow classify",
+    .short_help =
+    "set flow classify interface <int> [ip4-table <index>]\n"
+    "  [ip6-table <index>] [del]",
+    .function = set_flow_classify_command_fn,
+};
+
+static uword
+unformat_table_type (unformat_input_t * input, va_list * va)
+{
+  u32 * r = va_arg (*va, u32 *);
+  u32 tid;
+
+  if (unformat (input, "ip4"))
+    tid = FLOW_CLASSIFY_TABLE_IP4;
+  else if (unformat (input, "ip6"))
+    tid = FLOW_CLASSIFY_TABLE_IP6;
+  else
+    return 0;
+
+  *r = tid;
+  return 1;
+}
+static clib_error_t *
+show_flow_classify_command_fn (vlib_main_t * vm,
+                               unformat_input_t * input,
+                               vlib_cli_command_t * cmd)
+{
+  flow_classify_main_t * fcm = &flow_classify_main;
+  u32 type = FLOW_CLASSIFY_N_TABLES;
+  u32 * vec_tbl;
+  int i;
+
+  if (unformat (input, "type %U", unformat_table_type, &type))
+    ;
+  else
+    return clib_error_return (0, "Type must be specified.");;
+
+  if (type == FLOW_CLASSIFY_N_TABLES)
+    return clib_error_return (0, "Invalid table type.");
+
+  vec_tbl = fcm->classify_table_index_by_sw_if_index[type];
+
+  if (vec_len(vec_tbl))
+      vlib_cli_output (vm, "%10s%20s\t\t%s", "Intfc idx", "Classify table",
+                       "Interface name");
+  else
+    vlib_cli_output (vm, "No tables configured.");
+
+  for (i = 0; i < vec_len (vec_tbl); i++)
+    {
+      if (vec_elt(vec_tbl, i) == ~0)
+        continue;
+
+      vlib_cli_output (vm, "%10d%20d\t\t%U", i, vec_elt(vec_tbl, i),
+                       format_vnet_sw_if_index_name, fcm->vnet_main, i);
+    }
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (show_flow_classify_command, static) = {
+    .path = "show classify flow",
+    .short_help = "show classify flow type [ip4|ip6]",
+    .function = show_flow_classify_command_fn,
+};
diff --git a/vnet/vnet/classify/flow_classify.h b/vnet/vnet/classify/flow_classify.h
new file mode 100644 (file)
index 0000000..3ae04cd
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_vnet_flow_classify_h__
+#define __included_vnet_flow_classify_h__
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/classify/vnet_classify.h>
+
+typedef enum {
+  FLOW_CLASSIFY_TABLE_IP4,
+  FLOW_CLASSIFY_TABLE_IP6,
+  FLOW_CLASSIFY_N_TABLES,
+} flow_classify_table_id_t;
+
+typedef enum {
+  FLOW_CLASSIFY_NEXT_INDEX_DROP,
+  FLOW_CLASSIFY_NEXT_INDEX_N_NEXT,
+} flow_classify_next_index_t;
+
+typedef struct {
+  /* Classifier table vectors */
+  u32 * classify_table_index_by_sw_if_index [FLOW_CLASSIFY_N_TABLES];
+
+  /* Convenience variables */
+  vlib_main_t * vlib_main;
+  vnet_main_t * vnet_main;
+  vnet_classify_main_t * vnet_classify_main;
+  vnet_config_main_t * vnet_config_main [FLOW_CLASSIFY_N_TABLES];
+} flow_classify_main_t;
+
+flow_classify_main_t flow_classify_main;
+
+int vnet_set_flow_classify_intfc (vlib_main_t * vm, u32 sw_if_index,
+                                  u32 ip4_table_index, u32 ip6_table_index,
+                                  u32 is_add);
+
+#endif /* __included_vnet_flow_classify_h__ */
diff --git a/vnet/vnet/classify/flow_classify_node.c b/vnet/vnet/classify/flow_classify_node.c
new file mode 100644 (file)
index 0000000..aa3bce5
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/classify/flow_classify.h>
+#include <vnet/classify/vnet_classify.h>
+
+typedef struct {
+  u32 sw_if_index;
+  u32 next_index;
+  u32 table_index;
+  u32 offset;
+} flow_classify_trace_t;
+
+static u8 *
+format_flow_classify_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  flow_classify_trace_t * t = va_arg (*args, flow_classify_trace_t *);
+
+  s = format (s, "FLOW_CLASSIFY: sw_if_index %d next %d table %d offset %d",
+              t->sw_if_index, t->next_index, t->table_index, t->offset);
+  return s;
+}
+
+#define foreach_flow_classify_error                 \
+_(MISS, "Flow classify misses")                     \
+_(HIT, "Flow classify hits")                        \
+_(CHAIN_HIT, "Flow classify hits after chain walk") \
+_(DROP, "Flow classify action drop")
+
+typedef enum {
+#define _(sym,str) FLOW_CLASSIFY_ERROR_##sym,
+  foreach_flow_classify_error
+#undef _
+  FLOW_CLASSIFY_N_ERROR,
+} flow_classify_error_t;
+
+static char * flow_classify_error_strings[] = {
+#define _(sym,string) string,
+  foreach_flow_classify_error
+#undef _
+};
+
+static inline uword
+flow_classify_inline (vlib_main_t * vm,
+                      vlib_node_runtime_t * node,
+                      vlib_frame_t * frame,
+                      flow_classify_table_id_t tid)
+{
+  u32 n_left_from, * from, * to_next;
+  flow_classify_next_index_t next_index;
+  flow_classify_main_t * fcm = &flow_classify_main;
+  vnet_classify_main_t * vcm = fcm->vnet_classify_main;
+  f64 now = vlib_time_now (vm);
+  u32 hits = 0;
+  u32 misses = 0;
+  u32 chain_hits = 0;
+  u32 drop = 0;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+
+  /* First pass: compute hashes */
+  while (n_left_from > 2)
+    {
+      vlib_buffer_t * b0, * b1;
+      u32 bi0, bi1;
+      u8 * h0, * h1;
+      u32 sw_if_index0, sw_if_index1;
+      u32 table_index0, table_index1;
+      vnet_classify_table_t * t0, * t1;
+
+      /* Prefetch next iteration */
+      {
+        vlib_buffer_t * p1, * p2;
+
+        p1 = vlib_get_buffer (vm, from[1]);
+        p2 = vlib_get_buffer (vm, from[2]);
+
+        vlib_prefetch_buffer_header (p1, STORE);
+        CLIB_PREFETCH (p1->data, CLIB_CACHE_LINE_BYTES, STORE);
+        vlib_prefetch_buffer_header (p2, STORE);
+        CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
+      }
+
+      bi0 = from[0];
+      b0 = vlib_get_buffer (vm, bi0);
+      h0 = b0->data;
+
+      bi1 = from[1];
+      b1 = vlib_get_buffer (vm, bi1);
+      h1 = b1->data;
+
+      sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+      table_index0 = fcm->classify_table_index_by_sw_if_index[tid][sw_if_index0];
+
+      sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
+      table_index1 = fcm->classify_table_index_by_sw_if_index[tid][sw_if_index1];
+
+      t0 = pool_elt_at_index (vcm->tables, table_index0);
+
+      t1 = pool_elt_at_index (vcm->tables, table_index1);
+
+      vnet_buffer(b0)->l2_classify.hash =
+        vnet_classify_hash_packet (t0, (u8 *) h0);
+
+      vnet_classify_prefetch_bucket (t0, vnet_buffer(b0)->l2_classify.hash);
+
+      vnet_buffer(b1)->l2_classify.hash =
+        vnet_classify_hash_packet (t1, (u8 *) h1);
+
+      vnet_classify_prefetch_bucket (t1, vnet_buffer(b1)->l2_classify.hash);
+
+      vnet_buffer(b0)->l2_classify.table_index = table_index0;
+
+      vnet_buffer(b1)->l2_classify.table_index = table_index1;
+
+      from += 2;
+      n_left_from -= 2;
+    }
+
+  while (n_left_from > 0)
+    {
+      vlib_buffer_t * b0;
+      u32 bi0;
+      u8 * h0;
+      u32 sw_if_index0;
+      u32 table_index0;
+      vnet_classify_table_t * t0;
+
+      bi0 = from[0];
+      b0 = vlib_get_buffer (vm, bi0);
+      h0 = b0->data;
+
+      sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+      table_index0 = fcm->classify_table_index_by_sw_if_index[tid][sw_if_index0];
+
+      t0 = pool_elt_at_index (vcm->tables, table_index0);
+      vnet_buffer(b0)->l2_classify.hash =
+        vnet_classify_hash_packet (t0, (u8 *) h0);
+
+      vnet_buffer(b0)->l2_classify.table_index = table_index0;
+      vnet_classify_prefetch_bucket (t0, vnet_buffer(b0)->l2_classify.hash);
+
+      from++;
+      n_left_from--;
+    }
+
+  next_index = node->cached_next_index;
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      /* Not enough load/store slots to dual loop... */
+      while (n_left_from > 0 && n_left_to_next > 0)
+        {
+          u32 bi0;
+          vlib_buffer_t * b0;
+          u32 next0 = FLOW_CLASSIFY_NEXT_INDEX_DROP;
+          u32 table_index0;
+          vnet_classify_table_t * t0;
+          vnet_classify_entry_t * e0;
+          u64 hash0;
+          u8 * h0;
+
+          /* Stride 3 seems to work best */
+          if (PREDICT_TRUE (n_left_from > 3))
+            {
+              vlib_buffer_t * p1 = vlib_get_buffer(vm, from[3]);
+              vnet_classify_table_t * tp1;
+              u32 table_index1;
+              u64 phash1;
+
+              table_index1 = vnet_buffer(p1)->l2_classify.table_index;
+
+              if (PREDICT_TRUE (table_index1 != ~0))
+                {
+                  tp1 = pool_elt_at_index (vcm->tables, table_index1);
+                  phash1 = vnet_buffer(p1)->l2_classify.hash;
+                  vnet_classify_prefetch_entry (tp1, phash1);
+                }
+            }
+
+          /* Speculatively enqueue b0 to the current next frame */
+          bi0 = from[0];
+          to_next[0] = bi0;
+          from += 1;
+          to_next += 1;
+          n_left_from -= 1;
+          n_left_to_next -= 1;
+
+          b0 = vlib_get_buffer (vm, bi0);
+          h0 = b0->data;
+          table_index0 = vnet_buffer(b0)->l2_classify.table_index;
+          e0 = 0;
+          t0 = 0;
+
+          vnet_get_config_data (fcm->vnet_config_main[tid],
+                                &b0->current_config_index,
+                                &next0,
+                                /* # bytes of config data */ 0);
+
+          if (PREDICT_TRUE(table_index0 != ~0))
+            {
+              hash0 = vnet_buffer(b0)->l2_classify.hash;
+              t0 = pool_elt_at_index (vcm->tables, table_index0);
+              e0 = vnet_classify_find_entry (t0, (u8 *) h0, hash0, now);
+
+              if (e0)
+                {
+                  hits++;
+                }
+              else
+                {
+                  while (1)
+                    {
+                      if (PREDICT_TRUE(t0->next_table_index != ~0))
+                        {
+                          t0 = pool_elt_at_index (vcm->tables,
+                                                  t0->next_table_index);
+                        }
+                      else
+                        {
+                          misses++;
+                          break;
+                        }
+
+                      hash0 = vnet_classify_hash_packet (t0, (u8 *) h0);
+                      e0 = vnet_classify_find_entry (t0, (u8 *) h0, hash0, now);
+                      if (e0)
+                        {
+                          hits++;
+                          chain_hits++;
+                          break;
+                        }
+                    }
+                }
+            }
+          if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+            {
+              flow_classify_trace_t * t =
+                vlib_add_trace (vm, node, b0, sizeof (*t));
+              t->sw_if_index = vnet_buffer(b0)->sw_if_index[VLIB_RX];
+              t->next_index = next0;
+              t->table_index = t0 ? t0 - vcm->tables : ~0;
+              t->offset = e0 ? vnet_classify_get_offset (t0, e0): ~0;
+            }
+
+          /* Verify speculative enqueue, maybe switch current next frame */
+          vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                           n_left_to_next, bi0, next0);
+        }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  vlib_node_increment_counter (vm, node->node_index,
+                               FLOW_CLASSIFY_ERROR_MISS,
+                               misses);
+  vlib_node_increment_counter (vm, node->node_index,
+                               FLOW_CLASSIFY_ERROR_HIT,
+                               hits);
+  vlib_node_increment_counter (vm, node->node_index,
+                               FLOW_CLASSIFY_ERROR_CHAIN_HIT,
+                               chain_hits);
+  vlib_node_increment_counter (vm, node->node_index,
+                               FLOW_CLASSIFY_ERROR_DROP,
+                               drop);
+
+  return frame->n_vectors;
+}
+
+static uword
+ip4_flow_classify (vlib_main_t * vm,
+                   vlib_node_runtime_t * node,
+                   vlib_frame_t * frame)
+{
+  return flow_classify_inline(vm, node, frame, FLOW_CLASSIFY_TABLE_IP4);
+}
+
+VLIB_REGISTER_NODE (ip4_flow_classify_node) = {
+  .function = ip4_flow_classify,
+  .name = "ip4-flow-classify",
+  .vector_size = sizeof (u32),
+  .format_trace = format_flow_classify_trace,
+  .n_errors = ARRAY_LEN(flow_classify_error_strings),
+  .error_strings = flow_classify_error_strings,
+  .n_next_nodes = FLOW_CLASSIFY_NEXT_INDEX_N_NEXT,
+  .next_nodes = {
+    [FLOW_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (ip4_flow_classify_node, ip4_flow_classify);
+
+static uword
+ip6_flow_classify (vlib_main_t * vm,
+                   vlib_node_runtime_t * node,
+                   vlib_frame_t * frame)
+{
+  return flow_classify_inline(vm, node, frame, FLOW_CLASSIFY_TABLE_IP6);
+}
+
+VLIB_REGISTER_NODE (ip6_flow_classify_node) = {
+  .function = ip6_flow_classify,
+  .name = "ip6-flow-classify",
+  .vector_size = sizeof (u32),
+  .format_trace = format_flow_classify_trace,
+  .n_errors = ARRAY_LEN(flow_classify_error_strings),
+  .error_strings = flow_classify_error_strings,
+  .n_next_nodes = FLOW_CLASSIFY_NEXT_INDEX_N_NEXT,
+  .next_nodes = {
+    [FLOW_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (ip6_flow_classify_node, ip6_flow_classify);
+
+
+static clib_error_t *
+flow_classify_init (vlib_main_t *vm)
+{
+  flow_classify_main_t * fcm = &flow_classify_main;
+
+  fcm->vlib_main = vm;
+  fcm->vnet_main = vnet_get_main();
+  fcm->vnet_classify_main = &vnet_classify_main;
+
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (flow_classify_init);
index d3db4de..745ce1e 100644 (file)
@@ -128,6 +128,8 @@ typedef struct ip4_main_t {
   /** Built-in unicast feature path index, see @ref ip_feature_init_cast()  */
   u32 ip4_unicast_rx_feature_policer_classify;
   /** Built-in unicast feature path index, see @ref ip_feature_init_cast()  */
+  u32 ip4_unicast_rx_feature_flow_classify;
+  /** Built-in unicast feature path indix, see @ref ip_feature_init_cast()  */
   u32 ip4_unicast_rx_feature_ipsec;
   /** Built-in unicast feature path index, see @ref ip_feature_init_cast()  */
   u32 ip4_unicast_rx_feature_vpath;
index 72c9fb0..e997366 100644 (file)
@@ -797,6 +797,12 @@ ip4_add_del_interface_address (vlib_main_t * vm, u32 sw_if_index,
 }
 
 /* Built-in ip4 unicast rx feature path definition */
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_flow_classify, static) = {
+  .node_name = "ip4-flow-classify",
+  .runs_before = ORDER_CONSTRAINTS {"ip4-inacl", 0},
+  .feature_index = &ip4_main.ip4_unicast_rx_feature_flow_classify,
+};
+
 VNET_IP4_UNICAST_FEATURE_INIT (ip4_inacl, static) = {
   .node_name = "ip4-inacl", 
   .runs_before = ORDER_CONSTRAINTS {"ip4-source-check-via-rx", 0},
index 58bc7f8..b80317e 100644 (file)
@@ -166,6 +166,7 @@ typedef struct ip6_main_t {
   /* Built-in unicast feature path indices, see ip_feature_init_cast(...)  */
   u32 ip6_unicast_rx_feature_check_access;
   u32 ip6_unicast_rx_feature_policer_classify;
+  u32 ip6_unicast_rx_feature_flow_classify;
   u32 ip6_unicast_rx_feature_ipsec;
   u32 ip6_unicast_rx_feature_l2tp_decap;
   u32 ip6_unicast_rx_feature_vpath;
index 4833c11..4db6be5 100644 (file)
@@ -576,6 +576,12 @@ ip6_sw_interface_admin_up_down (vnet_main_t * vnm,
 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ip6_sw_interface_admin_up_down);
 
 /* Built-in ip6 unicast rx feature path definition */
+VNET_IP6_UNICAST_FEATURE_INIT (ip6_flow_classify, static) = {
+  .node_name = "ip6-flow-classify",
+  .runs_before = ORDER_CONSTRAINTS {"ip6-inacl", 0},
+  .feature_index = &ip6_main.ip6_unicast_rx_feature_flow_classify,
+};
+
 VNET_IP6_UNICAST_FEATURE_INIT (ip6_inacl, static) = {
   .node_name = "ip6-inacl", 
   .runs_before = ORDER_CONSTRAINTS {"ip6-policer-classify", 0},
index 02da664..603d1f7 100644 (file)
@@ -136,6 +136,9 @@ do {                                                \
   if ((error = vlib_call_init_function (vm, policer_classify_init)))
     return error;
 
+  if ((error = vlib_call_init_function (vm, flow_classify_init)))
+    return error;
+
   return error;
 }
 
index 6874c7d..78e01c6 100644 (file)
@@ -35,6 +35,7 @@
 #include <vnet/l2/l2_vtr.h>
 #include <vnet/classify/input_acl.h>
 #include <vnet/classify/policer_classify.h>
+#include <vnet/classify/flow_classify.h>
 #include <vnet/mpls/mpls.h>
 #if DPDK > 0
 #include <vnet/ipsec/ipsec.h>
@@ -456,7 +457,7 @@ unformat_policer_action_type (unformat_input_t * input, va_list * va)
 }
 
 uword
-unformat_classify_table_type (unformat_input_t * input, va_list * va)
+unformat_policer_classify_table_type (unformat_input_t * input, va_list * va)
 {
   u32 *r = va_arg (*va, u32 *);
   u32 tid;
@@ -474,6 +475,23 @@ unformat_classify_table_type (unformat_input_t * input, va_list * va)
   return 1;
 }
 
+uword
+unformat_flow_classify_table_type (unformat_input_t * input, va_list * va)
+{
+  u32 *r = va_arg (*va, u32 *);
+  u32 tid;
+
+  if (unformat (input, "ip4"))
+    tid = FLOW_CLASSIFY_TABLE_IP4;
+  else if (unformat (input, "ip6"))
+    tid = FLOW_CLASSIFY_TABLE_IP6;
+  else
+    return 0;
+
+  *r = tid;
+  return 1;
+}
+
 u8 *
 format_ip4_address (u8 * s, va_list * args)
 {
@@ -3370,6 +3388,35 @@ static void vl_api_ipsec_gre_add_del_tunnel_reply_t_handler_json
   vam->result_ready = 1;
 }
 
+static void vl_api_flow_classify_details_t_handler
+  (vl_api_flow_classify_details_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+
+  fformat (vam->ofp, "%10d%20d\n", ntohl (mp->sw_if_index),
+          ntohl (mp->table_index));
+}
+
+static void vl_api_flow_classify_details_t_handler_json
+  (vl_api_flow_classify_details_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  vat_json_node_t *node;
+
+  if (VAT_JSON_ARRAY != vam->json_tree.type)
+    {
+      ASSERT (VAT_JSON_NONE == vam->json_tree.type);
+      vat_json_init_array (&vam->json_tree);
+    }
+  node = vat_json_array_add (&vam->json_tree);
+
+  vat_json_init_object (node);
+  vat_json_object_add_uint (node, "sw_if_index", ntohl (mp->sw_if_index));
+  vat_json_object_add_uint (node, "table_index", ntohl (mp->table_index));
+}
+
+
+
 #define vl_api_vnet_ip4_fib_counters_t_endian vl_noop_handler
 #define vl_api_vnet_ip4_fib_counters_t_print vl_noop_handler
 #define vl_api_vnet_ip6_fib_counters_t_endian vl_noop_handler
@@ -3477,6 +3524,7 @@ _(netmap_delete_reply)                                  \
 _(set_ipfix_exporter_reply)                             \
 _(set_ipfix_classify_stream_reply)                      \
 _(ipfix_classify_table_add_del_reply)                   \
+_(flow_classify_set_interface_reply)                    \
 _(pg_capture_reply)                                     \
 _(pg_enable_disable_reply)                              \
 _(ip_source_and_port_range_check_add_del_reply)         \
@@ -3706,6 +3754,8 @@ _(SET_IPFIX_CLASSIFY_STREAM_REPLY, set_ipfix_classify_stream_reply)     \
 _(IPFIX_CLASSIFY_STREAM_DETAILS, ipfix_classify_stream_details)         \
 _(IPFIX_CLASSIFY_TABLE_ADD_DEL_REPLY, ipfix_classify_table_add_del_reply) \
 _(IPFIX_CLASSIFY_TABLE_DETAILS, ipfix_classify_table_details)           \
+_(FLOW_CLASSIFY_SET_INTERFACE_REPLY, flow_classify_set_interface_reply) \
+_(FLOW_CLASSIFY_DETAILS, flow_classify_details)                         \
 _(GET_NEXT_INDEX_REPLY, get_next_index_reply)                           \
 _(PG_CREATE_INTERFACE_REPLY, pg_create_interface_reply)                 \
 _(PG_CAPTURE_REPLY, pg_capture_reply)                                   \
@@ -13999,7 +14049,7 @@ api_policer_classify_dump (vat_main_t * vam)
   f64 timeout = ~0;
   u8 type = POLICER_CLASSIFY_N_TABLES;
 
-  if (unformat (i, "type %U", unformat_classify_table_type, &type))
+  if (unformat (i, "type %U", unformat_policer_classify_table_type, &type))
     ;
   else
     {
@@ -15355,6 +15405,95 @@ api_l2_interface_pbb_tag_rewrite (vat_main_t * vam)
   return 0;
 }
 
+static int
+api_flow_classify_set_interface (vat_main_t * vam)
+{
+  unformat_input_t *i = vam->input;
+  vl_api_flow_classify_set_interface_t *mp;
+  f64 timeout;
+  u32 sw_if_index;
+  int sw_if_index_set;
+  u32 ip4_table_index = ~0;
+  u32 ip6_table_index = ~0;
+  u8 is_add = 1;
+
+  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
+       sw_if_index_set = 1;
+      else if (unformat (i, "sw_if_index %d", &sw_if_index))
+       sw_if_index_set = 1;
+      else if (unformat (i, "del"))
+       is_add = 0;
+      else if (unformat (i, "ip4-table %d", &ip4_table_index))
+       ;
+      else if (unformat (i, "ip6-table %d", &ip6_table_index))
+       ;
+      else
+       {
+         clib_warning ("parse error '%U'", format_unformat_error, i);
+         return -99;
+       }
+    }
+
+  if (sw_if_index_set == 0)
+    {
+      errmsg ("missing interface name or sw_if_index\n");
+      return -99;
+    }
+
+  M (FLOW_CLASSIFY_SET_INTERFACE, flow_classify_set_interface);
+
+  mp->sw_if_index = ntohl (sw_if_index);
+  mp->ip4_table_index = ntohl (ip4_table_index);
+  mp->ip6_table_index = ntohl (ip6_table_index);
+  mp->is_add = is_add;
+
+  S;
+  W;
+  /* NOTREACHED */
+  return 0;
+}
+
+static int
+api_flow_classify_dump (vat_main_t * vam)
+{
+  unformat_input_t *i = vam->input;
+  vl_api_flow_classify_dump_t *mp;
+  f64 timeout = ~0;
+  u8 type = FLOW_CLASSIFY_N_TABLES;
+
+  if (unformat (i, "type %U", unformat_flow_classify_table_type, &type))
+    ;
+  else
+    {
+      errmsg ("classify table type must be specified\n");
+      return -99;
+    }
+
+  if (!vam->json_output)
+    {
+      fformat (vam->ofp, "%10s%20s\n", "Intfc idx", "Classify table");
+    }
+
+  M (FLOW_CLASSIFY_DUMP, flow_classify_dump);
+  mp->type = type;
+  /* send it... */
+  S;
+
+  /* Use a control ping for synchronization */
+  {
+    vl_api_control_ping_t *mp;
+    M (CONTROL_PING, control_ping);
+    S;
+  }
+  /* Wait for a reply... */
+  W;
+
+  /* NOTREACHED */
+  return 0;
+}
+
 static int
 q_or_quit (vat_main_t * vam)
 {
@@ -15952,7 +16091,10 @@ _(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]")
+_(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]")
 
 /* List of command functions, CLI names map directly to functions */
 #define foreach_cli_function                                    \
index a738249..0ef6966 100644 (file)
@@ -68,6 +68,7 @@
 #include <vnet/classify/vnet_classify.h>
 #include <vnet/classify/input_acl.h>
 #include <vnet/classify/policer_classify.h>
+#include <vnet/classify/flow_classify.h>
 #include <vnet/l2/l2_classify.h>
 #include <vnet/vxlan/vxlan.h>
 #include <vnet/gre/gre.h>
@@ -425,7 +426,9 @@ _(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)
+_(PUNT, punt)                                                           \
+_(FLOW_CLASSIFY_SET_INTERFACE, flow_classify_set_interface)             \
+_(FLOW_CLASSIFY_DUMP, flow_classify_dump)
 
 #define QUOTE_(x) #x
 #define QUOTE(x) QUOTE_(x)
@@ -8526,6 +8529,73 @@ vl_api_punt_t_handler (vl_api_punt_t * mp)
   REPLY_MACRO (VL_API_PUNT_REPLY);
 }
 
+static void
+  vl_api_flow_classify_set_interface_t_handler
+  (vl_api_flow_classify_set_interface_t * mp)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vl_api_flow_classify_set_interface_reply_t *rmp;
+  int rv;
+  u32 sw_if_index, ip4_table_index, ip6_table_index;
+
+  ip4_table_index = ntohl (mp->ip4_table_index);
+  ip6_table_index = ntohl (mp->ip6_table_index);
+  sw_if_index = ntohl (mp->sw_if_index);
+
+  VALIDATE_SW_IF_INDEX (mp);
+
+  rv = vnet_set_flow_classify_intfc (vm, sw_if_index, ip4_table_index,
+                                    ip6_table_index, mp->is_add);
+
+  BAD_SW_IF_INDEX_LABEL;
+
+  REPLY_MACRO (VL_API_FLOW_CLASSIFY_SET_INTERFACE_REPLY);
+}
+
+static void
+send_flow_classify_details (u32 sw_if_index,
+                           u32 table_index,
+                           unix_shared_memory_queue_t * q, u32 context)
+{
+  vl_api_flow_classify_details_t *mp;
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  memset (mp, 0, sizeof (*mp));
+  mp->_vl_msg_id = ntohs (VL_API_FLOW_CLASSIFY_DETAILS);
+  mp->context = context;
+  mp->sw_if_index = htonl (sw_if_index);
+  mp->table_index = htonl (table_index);
+
+  vl_msg_api_send_shmem (q, (u8 *) & mp);
+}
+
+static void
+vl_api_flow_classify_dump_t_handler (vl_api_flow_classify_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+  flow_classify_main_t *pcm = &flow_classify_main;
+  u32 *vec_tbl;
+  int i;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+  if (q == 0)
+    return;
+
+  vec_tbl = pcm->classify_table_index_by_sw_if_index[mp->type];
+
+  if (vec_len (vec_tbl))
+    {
+      for (i = 0; i < vec_len (vec_tbl); i++)
+       {
+         if (vec_elt (vec_tbl, i) == ~0)
+           continue;
+
+         send_flow_classify_details (i, vec_elt (vec_tbl, i), q,
+                                     mp->context);
+       }
+    }
+}
+
 #define BOUNCE_HANDLER(nn)                                              \
 static void vl_api_##nn##_t_handler (                                   \
     vl_api_##nn##_t *mp)                                                \
index 718e564..096c1aa 100644 (file)
@@ -30,6 +30,7 @@
 #include <vnet/classify/policer_classify.h>
 #include <vnet/policer/xlate.h>
 #include <vnet/policer/policer.h>
+#include <vnet/classify/flow_classify.h>
 #include <vlib/vlib.h>
 #include <vlib/unix/unix.h>
 #include <vlibapi/api.h>
@@ -2771,6 +2772,23 @@ static void *vl_api_l2_interface_pbb_tag_rewrite_t_print
   FINISH;
 }
 
+static void *vl_api_flow_classify_set_interface_t_print
+  (vl_api_flow_classify_set_interface_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: flow_classify_set_interface ");
+  s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index));
+  if (mp->ip4_table_index != ~0)
+    s = format (s, "ip4-table %d ", ntohl (mp->ip4_table_index));
+  if (mp->ip6_table_index != ~0)
+    s = format (s, "ip6-table %d ", ntohl (mp->ip6_table_index));
+  if (mp->is_add == 0)
+    s = format (s, "del ");
+
+  FINISH;
+}
+
 static void *
 vl_api_punt_t_print (vl_api_punt_t * mp, void *handle)
 {
@@ -2792,6 +2810,26 @@ vl_api_punt_t_print (vl_api_punt_t * mp, void *handle)
   FINISH;
 }
 
+static void *vl_api_flow_classify_dump_t_print
+  (vl_api_flow_classify_dump_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: flow_classify_dump ");
+  switch (mp->type)
+    {
+    case FLOW_CLASSIFY_TABLE_IP4:
+      s = format (s, "type ip4 ");
+      break;
+    case FLOW_CLASSIFY_TABLE_IP6:
+      s = format (s, "type ip6 ");
+      break;
+    default:
+      break;
+    }
+
+  FINISH;
+}
 
 #define foreach_custom_print_no_arg_function                            \
 _(lisp_eid_table_vni_dump)                                              \
@@ -2955,7 +2993,9 @@ _(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)
+_(PUNT, punt)                                                           \
+_(FLOW_CLASSIFY_SET_INTERFACE, flow_classify_set_interface)             \
+_(FLOW_CLASSIFY_DUMP, flow_classify_dump)
   void
 vl_msg_api_custom_dump_configure (api_main_t * am)
 {
index 0f1b0a8..a48d345 100644 (file)
@@ -4791,6 +4791,57 @@ define ipfix_classify_table_details {
     u8 transport_protocol;
 };
 
+/** \brief Set/unset flow classify interface
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param sw_if_index - interface to set/unset flow classify
+    @param ip4_table_index - ip4 classify table index (~0 for skip)
+    @param ip6_table_index - ip6 classify table index (~0 for skip)
+    @param l2_table_index  -  l2 classify table index (~0 for skip)
+    @param is_add - Set if non-zero, else unset
+    Note: User is recommeneded to use just one valid table_index per call.
+          (ip4_table_index, ip6_table_index, or l2_table_index)
+*/
+define flow_classify_set_interface {
+    u32 client_index;
+    u32 context;
+    u32 sw_if_index;
+    u32 ip4_table_index;
+    u32 ip6_table_index;
+    u8  is_add;
+};
+
+/** \brief Set/unset flow classify interface response
+    @param context - sender context, to match reply w/ request
+    @param retval - return value for request
+*/
+define flow_classify_set_interface_reply {
+    u32 context;
+    i32 retval;
+};
+
+/** \brief Get list of flow classify interfaces and tables
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param type - classify table type
+*/
+define flow_classify_dump {
+    u32 client_index;
+    u32 context;
+    u8 type;
+};
+
+/** \brief Flow classify operational state response.
+    @param context - sender context, to match reply w/ request
+    @param sw_if_index - software interface index
+    @param table_index - classify table index
+*/
+define flow_classify_details {
+    u32 context;
+    u32 sw_if_index;
+    u32 table_index;
+};
+
 /** \brief Query relative index via node names
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request