api: ip: add IP_ROUTE_LOOKUP API

Add an IP_ROUTE_LOOKUP function that does either an exact match or
longest prefix match in a given fib table for a given prefix
returning the match if present.

Add API test.

Type: improvement
Signed-off-by: Christian Hopps <chopps@labn.net>
Change-ID: I67ec5a61079f4acf1349a9c646185f91f5f11806
diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api
index 5624bcf..42371c4 100644
--- a/src/vnet/ip/ip.api
+++ b/src/vnet/ip/ip.api
@@ -190,6 +190,32 @@
   vl_api_ip_route_t route;
 };
 
+/** \brief Lookup IP route from a table
+    @param client_index - opaque cookie to identify the sender
+    @param table_id - The IP table to look the route up in
+    @param exact - 0 for normal route lookup, 1 for exact match only
+    @param prefix - The prefix (or host) for route lookup.
+*/
+define ip_route_lookup
+{
+  u32 client_index;
+  u32 context;
+  u32 table_id;
+  u8 exact;
+  vl_api_prefix_t prefix;
+};
+
+/** \brief IP FIB table lookup response
+    @param retval - return code of the lookup
+    @param route - The route entry in the table if found
+*/
+define ip_route_lookup_reply
+{
+  u32 context;
+  i32 retval;
+  vl_api_ip_route_t route;
+};
+
 /** \brief Set the ip flow hash config for a fib request
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c
index d5fa2d3..b976eae 100644
--- a/src/vnet/ip/ip_api.c
+++ b/src/vnet/ip/ip_api.c
@@ -82,6 +82,7 @@
 _(IP_TABLE_REPLACE_END, ip_table_replace_end)                           \
 _(IP_TABLE_FLUSH, ip_table_flush)                                       \
 _(IP_ROUTE_ADD_DEL, ip_route_add_del)                                   \
+_(IP_ROUTE_LOOKUP, ip_route_lookup)                                     \
 _(IP_TABLE_ADD_DEL, ip_table_add_del)                                   \
 _(IP_PUNT_POLICE, ip_punt_police)                                       \
 _(IP_PUNT_REDIRECT, ip_punt_redirect)                                   \
@@ -574,6 +575,62 @@
 }
 
 void
+vl_api_ip_route_lookup_t_handler (vl_api_ip_route_lookup_t * mp)
+{
+  vl_api_ip_route_lookup_reply_t *rmp = NULL;
+  fib_route_path_t *rpaths = NULL, *rpath;
+  const fib_prefix_t *pfx = NULL;
+  fib_prefix_t lookup;
+  vl_api_fib_path_t *fp;
+  fib_node_index_t fib_entry_index;
+  u32 fib_index;
+  int npaths = 0;
+  int rv;
+
+  ip_prefix_decode (&mp->prefix, &lookup);
+  rv = fib_api_table_id_decode (lookup.fp_proto, ntohl (mp->table_id),
+				&fib_index);
+  if (PREDICT_TRUE (!rv))
+    {
+      if (mp->exact)
+	fib_entry_index = fib_table_lookup_exact_match (fib_index, &lookup);
+      else
+	fib_entry_index = fib_table_lookup (fib_index, &lookup);
+      if (fib_entry_index == FIB_NODE_INDEX_INVALID)
+	rv = VNET_API_ERROR_NO_SUCH_ENTRY;
+      else
+	{
+	  pfx = fib_entry_get_prefix (fib_entry_index);
+	  rpaths = fib_entry_encode (fib_entry_index);
+	  npaths = vec_len (rpaths);
+	}
+    }
+
+  /* *INDENT-OFF* */
+  REPLY_MACRO3_ZERO(VL_API_IP_ROUTE_LOOKUP_REPLY,
+                    npaths * sizeof (*fp),
+  ({
+    if (!rv)
+      {
+        ip_prefix_encode (pfx, &rmp->route.prefix);
+        rmp->route.table_id = mp->table_id;
+        rmp->route.n_paths = npaths;
+        rmp->route.stats_index = fib_table_entry_get_stats_index (fib_index, pfx);
+        rmp->route.stats_index = htonl (rmp->route.stats_index);
+
+        fp = rmp->route.paths;
+        vec_foreach (rpath, rpaths)
+          {
+            fib_api_path_encode (rpath, fp);
+            fp++;
+          }
+      }
+  }));
+  /* *INDENT-ON* */
+  vec_free (rpaths);
+}
+
+void
 ip_table_create (fib_protocol_t fproto,
 		 u32 table_id, u8 is_api, const u8 * name)
 {