diff mbox series

[ovs-dev,2/3] netdev-dummy: Add local route entries for IP addresses.

Message ID 20240220223547.2368878-3-i.maximets@ovn.org
State Accepted
Commit 7992a26ef47f4f5a1fe527cff918d3f10c9fc7fd
Headers show
Series Fix ignoring of IPv6 'local_ip' for native tunnels. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Ilya Maximets Feb. 20, 2024, 10:35 p.m. UTC
From: Ihar Hrachyshka <ihrachys@redhat.com>

To mimic what kernel routing subsystem does [1], add a local route
entry for every dummy IP address. This helps with OVN testing multiple
chassis on a single host and allows to run better unit tests for
userspace tunnels without adding route entries manually.  This is also
the only way to add 'local' route entries that are required for testing
'local_ip' functionality with native tunnels in userspace datapath
because route lookup will reject non-local source IPs.

There seems to be no way to explicitly remove an IP address from
netdev-dummy, hence no code path to handle route entry cleanup.
The port itself can be removed, but our tests do not normally do that.
Removal can be implemented later if necessary.

[1]: http://linux-ip.net/html/routing-tables.html#routing-table-local

"If the machine has several IP addresses on one Ethernet interface,
there will be a route to each locally hosted IP in the local routing
table. This is a normal side effect of bringing up an IP address on
an interface under linux."

Signed-off-by: Ihar Hrachyshka <ihrachys@redhat.com>
Co-authored-by: Ilya Maximets <i.maximets@ovn.org>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
---
 lib/netdev-dummy.c            | 17 +++++++-
 lib/ovs-router.c              | 14 +++++++
 lib/ovs-router.h              |  5 +++
 tests/nsh.at                  | 14 ++-----
 tests/ofproto-dpif.at         | 15 ++++---
 tests/packet-type-aware.at    | 21 ++++------
 tests/tunnel-push-pop-ipv6.at | 32 +++++++++------
 tests/tunnel-push-pop.at      | 75 ++++++++++++++++++++---------------
 tests/tunnel.at               | 18 ++++-----
 9 files changed, 126 insertions(+), 85 deletions(-)

Comments

Eelco Chaudron Feb. 21, 2024, 3:31 p.m. UTC | #1
On 20 Feb 2024, at 23:35, Ilya Maximets wrote:

> From: Ihar Hrachyshka <ihrachys@redhat.com>
>
> To mimic what kernel routing subsystem does [1], add a local route
> entry for every dummy IP address. This helps with OVN testing multiple
> chassis on a single host and allows to run better unit tests for
> userspace tunnels without adding route entries manually.  This is also
> the only way to add 'local' route entries that are required for testing
> 'local_ip' functionality with native tunnels in userspace datapath
> because route lookup will reject non-local source IPs.
>
> There seems to be no way to explicitly remove an IP address from
> netdev-dummy, hence no code path to handle route entry cleanup.
> The port itself can be removed, but our tests do not normally do that.
> Removal can be implemented later if necessary.
>
> [1]: http://linux-ip.net/html/routing-tables.html#routing-table-local
>
> "If the machine has several IP addresses on one Ethernet interface,
> there will be a route to each locally hosted IP in the local routing
> table. This is a normal side effect of bringing up an IP address on
> an interface under linux."
>
> Signed-off-by: Ihar Hrachyshka <ihrachys@redhat.com>
> Co-authored-by: Ilya Maximets <i.maximets@ovn.org>
> Signed-of-by: Ilya Maximets <i.maximets@ovn.org>

Thanks for the patch, the changes look good to me and the tests are passing.

Acked-by: Eelco Chaudron <echaudro@redhat.com>
diff mbox series

Patch

diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
index cd7e85a81..e8bbf8d51 100644
--- a/lib/netdev-dummy.c
+++ b/lib/netdev-dummy.c
@@ -39,6 +39,7 @@ 
 #include "pcap-file.h"
 #include "openvswitch/poll-loop.h"
 #include "openvswitch/shash.h"
+#include "ovs-router.h"
 #include "sset.h"
 #include "stream.h"
 #include "unaligned.h"
@@ -2084,11 +2085,20 @@  netdev_dummy_ip4addr(struct unixctl_conn *conn, int argc OVS_UNUSED,
 
     if (netdev && is_dummy_class(netdev->netdev_class)) {
         struct in_addr ip, mask;
+        struct in6_addr ip6;
+        uint32_t plen;
         char *error;
 
-        error = ip_parse_masked(argv[2], &ip.s_addr, &mask.s_addr);
+        error = ip_parse_cidr(argv[2], &ip.s_addr, &plen);
         if (!error) {
+            mask.s_addr = be32_prefix_mask(plen);
             netdev_dummy_add_in4(netdev, ip, mask);
+
+            /* Insert local route entry for the new address. */
+            in6_addr_set_mapped_ipv4(&ip6, ip.s_addr);
+            ovs_router_force_insert(0, &ip6, plen + 96, true, argv[1],
+                                    &in6addr_any, &ip6);
+
             unixctl_command_reply(conn, "OK");
         } else {
             unixctl_command_reply_error(conn, error);
@@ -2118,6 +2128,11 @@  netdev_dummy_ip6addr(struct unixctl_conn *conn, int argc OVS_UNUSED,
 
             mask = ipv6_create_mask(plen);
             netdev_dummy_add_in6(netdev, &ip6, &mask);
+
+            /* Insert local route entry for the new address. */
+            ovs_router_force_insert(0, &ip6, plen, true, argv[1],
+                                    &in6addr_any, &ip6);
+
             unixctl_command_reply(conn, "OK");
         } else {
             unixctl_command_reply_error(conn, error);
diff --git a/lib/ovs-router.c b/lib/ovs-router.c
index ca014d80e..3d84c9a30 100644
--- a/lib/ovs-router.c
+++ b/lib/ovs-router.c
@@ -330,6 +330,20 @@  ovs_router_insert(uint32_t mark, const struct in6_addr *ip_dst, uint8_t plen,
     }
 }
 
+/* The same as 'ovs_router_insert', but it adds the route even if updates
+ * from the system routing table are disabled.  Used for unit tests. */
+void
+ovs_router_force_insert(uint32_t mark, const struct in6_addr *ip_dst,
+                        uint8_t plen, bool local, const char output_bridge[],
+                        const struct in6_addr *gw,
+                        const struct in6_addr *prefsrc)
+{
+    uint8_t priority = local ? plen + 64 : plen;
+
+    ovs_router_insert__(mark, priority, local, ip_dst, plen,
+                        output_bridge, gw, prefsrc);
+}
+
 static void
 rt_entry_delete__(const struct cls_rule *cr)
 {
diff --git a/lib/ovs-router.h b/lib/ovs-router.h
index eb4ff85d9..d7dc7e55f 100644
--- a/lib/ovs-router.h
+++ b/lib/ovs-router.h
@@ -34,6 +34,11 @@  void ovs_router_insert(uint32_t mark, const struct in6_addr *ip_dst,
                        uint8_t plen, bool local,
                        const char output_bridge[], const struct in6_addr *gw,
                        const struct in6_addr *prefsrc);
+void ovs_router_force_insert(uint32_t mark, const struct in6_addr *ip_dst,
+                             uint8_t plen, bool local,
+                             const char output_bridge[],
+                             const struct in6_addr *gw,
+                             const struct in6_addr *prefsrc);
 void ovs_router_flush(void);
 
 void ovs_router_disable_system_routing_table(void);
diff --git a/tests/nsh.at b/tests/nsh.at
index 55296e559..0040a50b3 100644
--- a/tests/nsh.at
+++ b/tests/nsh.at
@@ -521,51 +521,45 @@  AT_CHECK([
         set interface vxlangpe32 type=vxlan options:exts=gpe options:remote_ip=30.0.0.2 options:packet_type=ptap ofport_request=3020
 
     ovs-appctl netdev-dummy/ip4addr br-p1 10.0.0.1/24
-    ovs-appctl ovs/route/add 10.0.0.0/24 br-p1
     ovs-appctl tnl/arp/set br-p1 10.0.0.1 $HWADDR_BRP1
     ovs-appctl tnl/arp/set br-p1 10.0.0.2 $HWADDR_BRP2
     ovs-appctl tnl/arp/set br-p1 10.0.0.3 $HWADDR_BRP3
 
     ovs-appctl netdev-dummy/ip4addr br-p2 20.0.0.2/24
-    ovs-appctl ovs/route/add 20.0.0.0/24 br-p2
     ovs-appctl tnl/arp/set br-p2 20.0.0.1 $HWADDR_BRP1
     ovs-appctl tnl/arp/set br-p2 20.0.0.2 $HWADDR_BRP2
     ovs-appctl tnl/arp/set br-p2 20.0.0.3 $HWADDR_BRP3
 
     ovs-appctl netdev-dummy/ip4addr br-p3 30.0.0.3/24
-    ovs-appctl ovs/route/add 30.0.0.0/24 br-p3
     ovs-appctl tnl/arp/set br-p3 30.0.0.1 $HWADDR_BRP1
     ovs-appctl tnl/arp/set br-p3 30.0.0.2 $HWADDR_BRP2
     ovs-appctl tnl/arp/set br-p3 30.0.0.3 $HWADDR_BRP3
 ], [0], [stdout])
 
 AT_CHECK([
-    ovs-appctl ovs/route/add 10.0.0.0/24 br-p1
     ovs-appctl tnl/arp/set br-p1 10.0.0.1 $HWADDR_BRP1
     ovs-appctl tnl/arp/set br-p1 10.0.0.2 $HWADDR_BRP2
     ovs-appctl tnl/arp/set br-p1 10.0.0.3 $HWADDR_BRP3
 ], [0], [stdout])
 
 AT_CHECK([
-    ovs-appctl ovs/route/add 20.0.0.0/24 br-p2
     ovs-appctl tnl/arp/set br-p2 20.0.0.1 $HWADDR_BRP1
     ovs-appctl tnl/arp/set br-p2 20.0.0.2 $HWADDR_BRP2
     ovs-appctl tnl/arp/set br-p2 20.0.0.3 $HWADDR_BRP3
 ], [0], [stdout])
 
 AT_CHECK([
-    ovs-appctl ovs/route/add 30.0.0.0/24 br-p3
     ovs-appctl tnl/arp/set br-p3 30.0.0.1 $HWADDR_BRP1
     ovs-appctl tnl/arp/set br-p3 30.0.0.2 $HWADDR_BRP2
     ovs-appctl tnl/arp/set br-p3 30.0.0.3 $HWADDR_BRP3
 ], [0], [stdout])
 
 AT_CHECK([
-    ovs-appctl ovs/route/show | grep User:
+    ovs-appctl ovs/route/show | grep Cached: | sort
 ], [0], [dnl
-User: 10.0.0.0/24 dev br-p1 SRC 10.0.0.1
-User: 20.0.0.0/24 dev br-p2 SRC 20.0.0.2
-User: 30.0.0.0/24 dev br-p3 SRC 30.0.0.3
+Cached: 10.0.0.0/24 dev br-p1 SRC 10.0.0.1 local
+Cached: 20.0.0.0/24 dev br-p2 SRC 20.0.0.2 local
+Cached: 30.0.0.0/24 dev br-p3 SRC 30.0.0.3 local
 ])
 
 AT_CHECK([
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index e305e7b9c..daeea7775 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -7653,12 +7653,14 @@  dummy@ovs-dummy: hit:0 missed:0
     vm1 5/3: (dummy: ifindex=2011)
 ])
 
-dnl set up route to 1.1.2.92 via br0 and action=normal
+dnl Add 1.1.2.92 to br0 and action=normal
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0], [0], [OK
-])
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+dnl Checking that a local route for added IP was successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl
+Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 local
+])
 
 dnl Prime ARP Cache for 1.1.2.92
 AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'])
@@ -7669,10 +7671,13 @@  ovs-vsctl \
    --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" agent=127.0.0.1 \
      header=128 sampling=1 polling=0
 
-dnl set up route to 192.168.1.2 via br0
+dnl Add 192.168.1.2 to br0,
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.168.1.1/16], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add 192.168.0.0/16 br0], [0], [OK
+dnl Checking that a local route for added IP was successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl
+Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 local
+Cached: 192.168.0.0/16 dev br0 SRC 192.168.1.1 local
 ])
 
 dnl add rule for int-br to force packet onto tunnel. There is no ifindex
diff --git a/tests/packet-type-aware.at b/tests/packet-type-aware.at
index 14cebf6ef..d634930fd 100644
--- a/tests/packet-type-aware.at
+++ b/tests/packet-type-aware.at
@@ -142,30 +142,27 @@  AT_CHECK([
 ### Setup GRE tunnels
 AT_CHECK([
     ovs-appctl netdev-dummy/ip4addr br-p1 10.0.0.1/24 &&
-    ovs-appctl ovs/route/add 10.0.0.0/24 br-p1 &&
     ovs-appctl tnl/arp/set br-p1 10.0.0.1 $HWADDR_BRP1 &&
     ovs-appctl tnl/arp/set br-p1 10.0.0.2 $HWADDR_BRP2 &&
     ovs-appctl tnl/arp/set br-p1 10.0.0.3 $HWADDR_BRP3 &&
 
     ovs-appctl netdev-dummy/ip4addr br-p2 20.0.0.2/24 &&
-    ovs-appctl ovs/route/add 20.0.0.0/24 br-p2 &&
     ovs-appctl tnl/arp/set br-p2 20.0.0.1 $HWADDR_BRP1 &&
     ovs-appctl tnl/arp/set br-p2 20.0.0.2 $HWADDR_BRP2 &&
     ovs-appctl tnl/arp/set br-p2 20.0.0.3 $HWADDR_BRP3 &&
 
     ovs-appctl netdev-dummy/ip4addr br-p3 30.0.0.3/24 &&
-    ovs-appctl ovs/route/add 30.0.0.0/24 br-p3 &&
     ovs-appctl tnl/arp/set br-p3 30.0.0.1 $HWADDR_BRP1 &&
     ovs-appctl tnl/arp/set br-p3 30.0.0.2 $HWADDR_BRP2 &&
     ovs-appctl tnl/arp/set br-p3 30.0.0.3 $HWADDR_BRP3
 ], [0], [ignore])
 
 AT_CHECK([
-    ovs-appctl ovs/route/show | grep User:
+    ovs-appctl ovs/route/show | grep Cached: | sort
 ], [0], [dnl
-User: 10.0.0.0/24 dev br-p1 SRC 10.0.0.1
-User: 20.0.0.0/24 dev br-p2 SRC 20.0.0.2
-User: 30.0.0.0/24 dev br-p3 SRC 30.0.0.3
+Cached: 10.0.0.0/24 dev br-p1 SRC 10.0.0.1 local
+Cached: 20.0.0.0/24 dev br-p2 SRC 20.0.0.2 local
+Cached: 30.0.0.0/24 dev br-p3 SRC 30.0.0.3 local
 ])
 
 AT_CHECK([
@@ -681,14 +678,13 @@  AT_CHECK([
 
 AT_CHECK([
     ovs-appctl netdev-dummy/ip4addr br2 10.0.0.1/24 &&
-    ovs-appctl ovs/route/add 10.0.0.0/24 br2 &&
     ovs-appctl tnl/arp/set br2 10.0.0.2 de:af:be:ef:ba:be
 ], [0], [ignore])
 
 AT_CHECK([
-    ovs-appctl ovs/route/show | grep User:
+    ovs-appctl ovs/route/show | grep Cached:
 ], [0], [dnl
-User: 10.0.0.0/24 dev br2 SRC 10.0.0.1
+Cached: 10.0.0.0/24 dev br2 SRC 10.0.0.1 local
 ])
 
 
@@ -955,7 +951,6 @@  AT_CHECK([
 
 AT_CHECK([
     ovs-appctl netdev-dummy/ip4addr br0 20.0.0.1/24 &&
-    ovs-appctl ovs/route/add 20.0.0.2/24 br0 &&
     ovs-appctl tnl/neigh/set br0 20.0.0.1 aa:bb:cc:00:00:01 &&
     ovs-appctl tnl/neigh/set br0 20.0.0.2 aa:bb:cc:00:00:02
 ], [0], [ignore])
@@ -963,9 +958,9 @@  AT_CHECK([
 ovs-appctl time/warp 1000
 
 AT_CHECK([
-    ovs-appctl ovs/route/show | grep User
+    ovs-appctl ovs/route/show | grep Cached:
 ],[0], [dnl
-User: 20.0.0.0/24 dev br0 SRC 20.0.0.1
+Cached: 20.0.0.0/24 dev br0 SRC 20.0.0.1 local
 ])
 
 AT_CHECK([
diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at
index a8dd28c5b..6d9ac6841 100644
--- a/tests/tunnel-push-pop-ipv6.at
+++ b/tests/tunnel-push-pop-ipv6.at
@@ -19,11 +19,12 @@  AT_CHECK([ovs-vsctl add-port int-br3 t3 -- set Interface t3 type=srv6 \
                        options:srv6_flowlabel=compute \
                        ], [0])
 
-dnl First setup dummy interface IP address, then add the route
-dnl so that tnl-port table can get valid IP address for the device.
+dnl Setup dummy interface IP address.
 AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/24], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add 2001:cafe::0/24 br0], [0], [OK
+dnl Checking that a local routes for added IPs were successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl
+Cached: 2001:ca00::/24 dev br0 SRC 2001:cafe::88 local
 ])
 AT_CHECK([ovs-appctl tnl/neigh/set br0 2001:cafe::91 aa:55:aa:55:00:01], [0], [OK
 ])
@@ -105,13 +106,15 @@  dummy@ovs-dummy: hit:0 missed:0
     t2 2/6: (ip6gre: remote_ip=2001:cafe::92)
 ])
 
-dnl First setup dummy interface IP address, then add the route
-dnl so that tnl-port table can get valid IP address for the device.
+dnl Setup dummy interface IP addresses.
 AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/24], [0], [OK
 ])
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add 2001:cafe::92/24 br0], [0], [OK
+dnl Checking that a local routes for added IPs were successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl
+Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 local
+Cached: 2001:ca00::/24 dev br0 SRC 2001:cafe::88 local
 ])
 
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
@@ -179,13 +182,15 @@  dummy@ovs-dummy: hit:0 missed:0
     t3 3/6: (ip6erspan: erspan_dir=1, erspan_hwid=0x7, erspan_ver=2, key=567, remote_ip=2001:cafe::93)
 ])
 
-dnl First setup dummy interface IP address, then add the route
-dnl so that tnl-port table can get valid IP address for the device.
+dnl Setup dummy interface IP addresses.
 AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/24], [0], [OK
 ])
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add 2001:cafe::92/24 br0], [0], [OK
+dnl Checking that a local routes for added IPs were successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl
+Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 local
+Cached: 2001:ca00::/24 dev br0 SRC 2001:cafe::88 local
 ])
 
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
@@ -316,14 +321,15 @@  srv6_sys (6) ref_cnt=1
 vxlan_sys_4789 (4789) ref_cnt=2
 ])
 
-
-dnl First setup dummy interface IP address, then add the route
-dnl so that tnl-port table can get valid IP address for the device.
+dnl Setup dummy interface IP addresses.
 AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/24], [0], [OK
 ])
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add 2001:cafe::92/24 br0], [0], [OK
+dnl Checking that a local routes for added IPs were successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl
+Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 local
+Cached: 2001:ca00::/24 dev br0 SRC 2001:cafe::88 local
 ])
 
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
index e51984fde..04d17b71f 100644
--- a/tests/tunnel-push-pop.at
+++ b/tests/tunnel-push-pop.at
@@ -30,17 +30,15 @@  dummy@ovs-dummy: hit:0 missed:0
     t4 5/3: (erspan: erspan_dir=flow, erspan_hwid=flow, erspan_idx=flow, erspan_ver=flow, key=56, remote_ip=flow)
 ])
 
-dnl First setup dummy interface IP address, then add the route
-dnl so that tnl-port table can get valid IP address for the device.
+dnl Setup dummy interface IP addresses.
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
 ])
 AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/24], [0], [OK
 ])
-
-AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0], [0], [OK
-])
-
-AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0 pkt_mark=1234], [0], [OK
+dnl Checking that a local routes for added IPs were successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl
+Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 local
+Cached: 2001:ca00::/24 dev br0 SRC 2001:cafe::88 local
 ])
 
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
@@ -237,18 +235,21 @@  dummy@ovs-dummy: hit:0 missed:0
     t8 9/2152: (gtpu: key=123, remote_ip=1.1.2.92)
 ])
 
-dnl First setup dummy interface IP address, then add the route
-dnl so that tnl-port table can get valid IP address for the device.
+dnl Setup dummy interface IP addresses.
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
 ])
 AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/24], [0], [OK
 ])
-
-AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0], [0], [OK
-])
-
+dnl Add a static route with a mark.
 AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0 pkt_mark=1234], [0], [OK
 ])
+dnl Checking that local routes for added IPs and the static route with a mark
+dnl were successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep br0 | sort], [0], [dnl
+Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 local
+Cached: 2001:ca00::/24 dev br0 SRC 2001:cafe::88 local
+User: 1.1.2.0/24 MARK 1234 dev br0 SRC 1.1.2.88
+])
 
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 
@@ -690,12 +691,12 @@  AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=geneve \
                        options:remote_ip=1.1.2.92 options:key=123 ofport_request=2 \
                        ])
 
-dnl First setup dummy interface IP address, then add the route
-dnl so that tnl-port table can get valid IP address for the device.
+dnl Setup dummy interface IP address.
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
 ])
-
-AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0], [0], [OK
+dnl Checking that a local route for added IP was successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl
+Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 local
 ])
 
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
@@ -731,11 +732,12 @@  AT_CHECK([ovs-vsctl add-port int-br t2 dnl
           -- set Interface t2 type=geneve options:remote_ip=1.1.2.92 dnl
                               options:key=123 ofport_request=2])
 
-dnl First setup dummy interface IP address, then add the route
-dnl so that tnl-port table can get valid IP address for the device.
+dnl Setup dummy interface IP address.
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0], [0], [OK
+dnl Checking that a local route for added IP was successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl
+Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 local
 ])
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 
@@ -796,8 +798,11 @@  dummy@ovs-dummy: hit:0 missed:0
 
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0], [0], [OK
+dnl Checking that a local route for added IP was successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl
+Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 local
 ])
+
 AT_CHECK([ovs-ofctl add-flow br0 'arp,priority=1,action=normal'])
 
 dnl Use arp reply to achieve tunnel next hop mac binding
@@ -840,11 +845,12 @@  AT_CHECK([ovs-vsctl add-port int-br t2 dnl
           -- set Interface t2 type=geneve options:remote_ip=1.1.2.92 dnl
                               options:key=123 ofport_request=2])
 
-dnl First setup dummy interface IP address, then add the route
-dnl so that tnl-port table can get valid IP address for the device.
+dnl Setup dummy interface IP address.
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0], [0], [OK
+dnl Checking that a local route for added IP was successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl
+Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 local
 ])
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 
@@ -908,10 +914,12 @@  AT_CHECK([ovs-vsctl set port p8  tag=42 dnl
                  -- set port br0 tag=42 dnl
                  -- set port p7  tag=200])
 
-dnl Set IP address and route for br0.
+dnl Set an IP address for br0.
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 10.0.0.2/24], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add 10.0.0.11/24 br0], [0], [OK
+dnl Checking that a local route for added IP was successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl
+Cached: 10.0.0.0/24 dev br0 SRC 10.0.0.2 local
 ])
 
 dnl Send an ARP reply to port b8 on br0, so that packets will be forwarded
@@ -953,10 +961,12 @@  AT_CHECK([ovs-vsctl add-port ovs-tun0 tun0 dnl
           -- add-port ovs-tun0 p7 dnl
           -- set interface p7 type=dummy ofport_request=7])
 
-dnl Set IP address and route for br0.
+dnl Set an IP address for br0.
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 10.0.0.2/24], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add 10.0.0.11/24 br0], [0], [OK
+dnl Checking that a local route for added IP was successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl
+Cached: 10.0.0.0/24 dev br0 SRC 10.0.0.2 local
 ])
 
 dnl Send an ARP reply to port b8 on br0, so that packets will be forwarded
@@ -1026,8 +1036,11 @@  dummy@ovs-dummy: hit:0 missed:0
 
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr vtep0 1.1.2.88/24], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 vtep0], [0], [OK
+dnl Checking that a local route for added IP was successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl
+Cached: 1.1.2.0/24 dev vtep0 SRC 1.1.2.88 local
 ])
+
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 AT_CHECK([ovs-ofctl add-flow int-br action=normal])
 
@@ -1044,10 +1057,6 @@  AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl
 1.1.2.92                                      f8:bc:12:44:34:b6   br0
 ])
 
-AT_CHECK([ovs-appctl ovs/route/show | tail -n+2 | sort], [0], [dnl
-User: 1.1.2.0/24 dev vtep0 SRC 1.1.2.88
-])
-
 dnl Check GRE tunnel pop.
 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),dnl
   eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:03),eth_type(0x0800),dnl
diff --git a/tests/tunnel.at b/tests/tunnel.at
index 282651ac7..71e7c2df4 100644
--- a/tests/tunnel.at
+++ b/tests/tunnel.at
@@ -524,11 +524,12 @@  dummy@ovs-dummy: hit:0 missed:0
     v2 3/3: (dummy-internal)
 ])
 
-dnl First setup dummy interface IP address, then add the route
-dnl so that tnl-port table can get valid IP address for the device.
+dnl Setup dummy interface IP address.
 AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 172.31.1.1/24], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add 172.31.1.0/24 br0], [0], [OK
+dnl Checking that a local route for added IP was successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl
+Cached: 172.31.1.0/24 dev br0 SRC 172.31.1.1 local
 ])
 
 dnl change the flow table to bump the internal table version
@@ -1276,15 +1277,12 @@  OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy \
                     ofport_request=2])
 OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
 
-dnl First setup dummy interface IP address, then add the route
-dnl so that tnl-port table can get valid IP address for the device.
+dnl Setup dummy interface IP address.
 AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 fc00::1/64], [0], [OK
 ])
-AT_CHECK([ovs-appctl ovs/route/add fc00::0/64 br0], [0], [OK
-])
-AT_CHECK([ovs-appctl ovs/route/show], [0], [dnl
-Route Table:
-User: fc00::/64 dev br0 SRC fc00::1
+dnl Checking that a local route for added IP was successfully installed.
+AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl
+Cached: fc00::/64 dev br0 SRC fc00::1 local
 ])
 
 AT_DATA([flows.txt], [dnl