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)
{
diff --git a/test/test_ip4.py b/test/test_ip4.py
index 6f8047c..43804c7 100644
--- a/test/test_ip4.py
+++ b/test/test_ip4.py
@@ -214,6 +214,80 @@
self.verify_capture(i, pkts)
+class TestIPv4RouteLookup(VppTestCase):
+ """ IPv4 Route Lookup Test Case """
+ routes = []
+
+ def route_lookup(self, prefix, exact):
+ return self.vapi.api(self.vapi.papi.ip_route_lookup,
+ {
+ 'table_id': 0,
+ 'exact': exact,
+ 'prefix': prefix,
+ })
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestIPv4RouteLookup, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestIPv4RouteLookup, cls).tearDownClass()
+
+ def setUp(self):
+ super(TestIPv4RouteLookup, self).setUp()
+
+ drop_nh = VppRoutePath("127.0.0.1", 0xffffffff,
+ type=FibPathType.FIB_PATH_TYPE_DROP)
+
+ # Add 3 routes
+ r = VppIpRoute(self, "1.1.0.0", 16, [drop_nh])
+ r.add_vpp_config()
+ self.routes.append(r)
+
+ r = VppIpRoute(self, "1.1.1.0", 24, [drop_nh])
+ r.add_vpp_config()
+ self.routes.append(r)
+
+ r = VppIpRoute(self, "1.1.1.1", 32, [drop_nh])
+ r.add_vpp_config()
+ self.routes.append(r)
+
+ def tearDown(self):
+ # Remove the routes we added
+ for r in self.routes:
+ r.remove_vpp_config()
+
+ super(TestIPv4RouteLookup, self).tearDown()
+
+ def test_exact_match(self):
+ # Verify we find the host route
+ prefix = "1.1.1.1/32"
+ result = self.route_lookup(prefix, True)
+ assert (prefix == str(result.route.prefix))
+
+ # Verify we find a middle prefix route
+ prefix = "1.1.1.0/24"
+ result = self.route_lookup(prefix, True)
+ assert (prefix == str(result.route.prefix))
+
+ # Verify we do not find an available LPM.
+ with self.vapi.assert_negative_api_retval():
+ self.route_lookup("1.1.1.2/32", True)
+
+ def test_longest_prefix_match(self):
+ # verify we find lpm
+ lpm_prefix = "1.1.1.0/24"
+ result = self.route_lookup("1.1.1.2/32", False)
+ assert (lpm_prefix == str(result.route.prefix))
+
+ # Verify we find the exact when not requested
+ result = self.route_lookup(lpm_prefix, False)
+ assert (lpm_prefix == str(result.route.prefix))
+
+ # Can't seem to delete the default route so no negative LPM test.
+
+
class TestIPv4IfAddrRoute(VppTestCase):
""" IPv4 Interface Addr Route Test Case """
diff --git a/test/test_ip6.py b/test/test_ip6.py
index c92ebb5..87346c2 100644
--- a/test/test_ip6.py
+++ b/test/test_ip6.py
@@ -979,6 +979,80 @@
self.assertEqual(mld.records_number, 4)
+class TestIPv6RouteLookup(VppTestCase):
+ """ IPv6 Route Lookup Test Case """
+ routes = []
+
+ def route_lookup(self, prefix, exact):
+ return self.vapi.api(self.vapi.papi.ip_route_lookup,
+ {
+ 'table_id': 0,
+ 'exact': exact,
+ 'prefix': prefix,
+ })
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestIPv6RouteLookup, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestIPv6RouteLookup, cls).tearDownClass()
+
+ def setUp(self):
+ super(TestIPv6RouteLookup, self).setUp()
+
+ drop_nh = VppRoutePath("::1", 0xffffffff,
+ type=FibPathType.FIB_PATH_TYPE_DROP)
+
+ # Add 3 routes
+ r = VppIpRoute(self, "2001:1111::", 32, [drop_nh])
+ r.add_vpp_config()
+ self.routes.append(r)
+
+ r = VppIpRoute(self, "2001:1111:2222::", 48, [drop_nh])
+ r.add_vpp_config()
+ self.routes.append(r)
+
+ r = VppIpRoute(self, "2001:1111:2222::1", 128, [drop_nh])
+ r.add_vpp_config()
+ self.routes.append(r)
+
+ def tearDown(self):
+ # Remove the routes we added
+ for r in self.routes:
+ r.remove_vpp_config()
+
+ super(TestIPv6RouteLookup, self).tearDown()
+
+ def test_exact_match(self):
+ # Verify we find the host route
+ prefix = "2001:1111:2222::1/128"
+ result = self.route_lookup(prefix, True)
+ assert (prefix == str(result.route.prefix))
+
+ # Verify we find a middle prefix route
+ prefix = "2001:1111:2222::/48"
+ result = self.route_lookup(prefix, True)
+ assert (prefix == str(result.route.prefix))
+
+ # Verify we do not find an available LPM.
+ with self.vapi.assert_negative_api_retval():
+ self.route_lookup("2001::2/128", True)
+
+ def test_longest_prefix_match(self):
+ # verify we find lpm
+ lpm_prefix = "2001:1111:2222::/48"
+ result = self.route_lookup("2001:1111:2222::2/128", False)
+ assert (lpm_prefix == str(result.route.prefix))
+
+ # Verify we find the exact when not requested
+ result = self.route_lookup(lpm_prefix, False)
+ assert (lpm_prefix == str(result.route.prefix))
+
+ # Can't seem to delete the default route so no negative LPM test.
+
+
class TestIPv6IfAddrRoute(VppTestCase):
""" IPv6 Interface Addr Route Test Case """