Add a new command `ovs-appctl ovs/router/rule/show` for printing OVS
internal routing rules database:

  ovs-appctl ovs/router/rule/show
  32763: from 7.7.7.17 lookup 20
  32764: from all lookup 15
  32765: from 7.7.7.7 lookup 10

The rules are sorted from the highest priority being the first to the
lowest being the last one.

Signed-off-by: Dima Chumak <dchu...@nvidia.com>
---
 Documentation/howto/userspace-tunneling.rst |  4 +
 NEWS                                        |  1 +
 lib/ovs-router.c                            | 93 +++++++++++++++++++++
 ofproto/ofproto-tnl-unixctl.man             |  4 +
 tests/ovs-router.at                         | 16 ++++
 tests/system-route.at                       | 80 ++++++++++++++++++
 6 files changed, 198 insertions(+)

diff --git a/Documentation/howto/userspace-tunneling.rst 
b/Documentation/howto/userspace-tunneling.rst
index 1dd34cd2f5e4..e443b85e67c9 100644
--- a/Documentation/howto/userspace-tunneling.rst
+++ b/Documentation/howto/userspace-tunneling.rst
@@ -207,6 +207,10 @@ To see all routes configured::
 
     $ ovs-appctl ovs/route/show [table=ID|all]
 
+To see all router rules configured::
+
+    $ ovs-appctl ovs/router/rule/show
+
 To delete route::
 
     $ ovs-appctl ovs/route/del <IP address>/<prefix length>
diff --git a/NEWS b/NEWS
index 97554be8e6b4..ed32c543559d 100644
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,7 @@ v3.6.0 - xx xxx xxxx
      * Added JSON output support to the 'ovs/route/show' command.
      * 'ovs/route/show': added new option, table=[ID|all], to list routes from
        a specific OVS table or all routes from all tables.
+     * Added a new sub-command, ovs/router/rule/show, to list OVS router rules.
    - ovs-vsctl:
      * Now exits with error code 160 (ERROR_BAD_ARGUMENTS) on Windows and
        65 (EX_DATAERR) on other platforms if it fails while waiting for
diff --git a/lib/ovs-router.c b/lib/ovs-router.c
index eaf688467389..6cbe08625f5f 100644
--- a/lib/ovs-router.c
+++ b/lib/ovs-router.c
@@ -843,6 +843,97 @@ ovs_router_show(struct unixctl_conn *conn, int argc 
OVS_UNUSED,
     }
 }
 
+static void
+ovs_router_rules_show_json(struct json *rule_entries)
+{
+    struct router_rule *rule;
+    struct ds ds;
+
+    PVECTOR_FOR_EACH (rule, &rules) {
+        struct json *entry = json_object_create();
+
+        json_object_put(entry, "priority", json_integer_create(rule->prio));
+        json_object_put(entry, "invert", json_boolean_create(rule->invert));
+        json_object_put(entry, "src_len", json_integer_create(rule->src_len));
+        json_object_put(entry, "lookup",
+                        json_integer_create(rule->lookup_table));
+
+        if (rule->src_len) {
+            ds_init(&ds);
+            ipv6_format_mapped(&rule->from_addr, &ds);
+            json_object_put_string(entry, "from", ds_cstr_ro(&ds));
+            ds_destroy(&ds);
+        } else {
+            json_object_put_string(entry, "from", "all");
+        }
+
+        json_array_add(rule_entries, entry);
+    }
+}
+
+static char *
+standard_table_name(uint32_t table)
+{
+    switch (table) {
+    case CLS_DEFAULT:
+        return "default";
+    case CLS_MAIN:
+        return "main";
+    case CLS_LOCAL:
+        return "local";
+    }
+
+    return NULL;
+}
+
+static void
+ovs_router_rules_show_text(struct ds *ds)
+{
+    struct router_rule *rule;
+
+    PVECTOR_FOR_EACH (rule, &rules) {
+        ds_put_format(ds, "%u: ", rule->prio);
+        if (rule->invert) {
+            ds_put_format(ds, "not ");
+        }
+        ds_put_format(ds, "from ");
+        if (rule->src_len) {
+            ipv6_format_mapped(&rule->from_addr, ds);
+            if (!((IN6_IS_ADDR_V4MAPPED(&rule->from_addr) &&
+                   rule->src_len == 32) || rule->src_len == 128)) {
+                ds_put_format(ds, "/%u ", rule->src_len);
+            }
+        } else {
+            ds_put_cstr(ds, "all");
+        }
+        ds_put_format(ds, " ");
+        if (route_table_is_standard_id(rule->lookup_table)) {
+            ds_put_format(ds, "lookup %s\n",
+                          standard_table_name(rule->lookup_table));
+        } else {
+            ds_put_format(ds, "lookup %u\n", rule->lookup_table);
+        }
+    }
+}
+
+static void
+ovs_router_rules_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                      const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+{
+    if (unixctl_command_get_output_format(conn) == UNIXCTL_OUTPUT_FMT_JSON) {
+        struct json *entries = json_array_create_empty();
+
+        ovs_router_rules_show_json(entries);
+        unixctl_command_reply_json(conn, entries);
+    } else {
+        struct ds ds = DS_EMPTY_INITIALIZER;
+
+        ovs_router_rules_show_text(&ds);
+        unixctl_command_reply(conn, ds_cstr(&ds));
+        ds_destroy(&ds);
+    }
+}
+
 static void
 ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc,
                       const char *argv[], void *aux OVS_UNUSED)
@@ -1021,6 +1112,8 @@ ovs_router_init(void)
         unixctl_command_register("ovs/route/lookup", "ip_addr "
                                  "[pkt_mark=mark]", 1, 2,
                                  ovs_router_lookup_cmd, NULL);
+        unixctl_command_register("ovs/route/rule/show", "", 0, 0,
+                                 ovs_router_rules_show, NULL);
         ovsthread_once_done(&once);
     }
 }
diff --git a/ofproto/ofproto-tnl-unixctl.man b/ofproto/ofproto-tnl-unixctl.man
index b9e4d99b2bf8..2ee17ddd4742 100644
--- a/ofproto/ofproto-tnl-unixctl.man
+++ b/ofproto/ofproto-tnl-unixctl.man
@@ -17,6 +17,10 @@ all routing tables is printed.
 .IP "\fBovs/route/del ip/plen [pkt_mark=mark]\fR"
 Delete ip/plen route from OVS routing table.
 .
+.IP "\fBovs/route/rule/show\fR"
+Print routing rules in OVS. This includes routing rules cached from the system
+routing policy database and user configured routing rules.
+.
 .IP "\fBtnl/neigh/show\fR"
 .IP "\fBtnl/arp/show\fR"
 OVS builds ARP cache by snooping are messages. This command shows
diff --git a/tests/ovs-router.at b/tests/ovs-router.at
index dac096cc1aec..450eebb28fb0 100644
--- a/tests/ovs-router.at
+++ b/tests/ovs-router.at
@@ -229,3 +229,19 @@ User: 2001:db8:babe::/64 dev br0 GW 2001:db8:cafe::2 SRC 
2001:db8:cafe::1
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([appctl - route/rule/show])
+AT_KEYWORDS([ovs_router])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy])
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.0.2.1/24], [0], [OK
+])
+
+dnl Check standard rules exist.
+AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl
+0: from all lookup local
+32766: from all lookup main
+32767: from all lookup default
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
diff --git a/tests/system-route.at b/tests/system-route.at
index 66bfd0e8ed2e..09b2861443cb 100644
--- a/tests/system-route.at
+++ b/tests/system-route.at
@@ -332,3 +332,83 @@ AT_CHECK([ovstest test-lib-route-table-dump | \
 ])
 
 AT_CLEANUP
+
+dnl Checks that OVS uses routes from non-standard tables when there is a rule
+dnl referencing the table.
+AT_SETUP([ovs-route - route tables + rules])
+AT_KEYWORDS([route])
+OVS_TRAFFIC_VSWITCHD_START()
+
+dnl Create tap port.
+on_exit 'ip link del p1-route'
+AT_CHECK([ip tuntap add name p1-route mode tap])
+AT_CHECK([ip link set p1-route up])
+
+dnl Add ip address.
+AT_CHECK([ip addr add 10.0.0.17/24 dev p1-route], [0], [stdout])
+
+dnl Add routes to a custom routing table with a source match rule and check
+dnl that OVS caches them.
+AT_CHECK([ip rule add from 10.0.0.1 lookup 42])
+AT_CHECK([ip rule show | grep -q 'from 10.0.0.1 lookup 42'])
+
+dnl Give the main thread a chance to act.
+AT_CHECK([ovs-appctl revalidator/wait])
+dnl Check that the rule is cached in OVS.
+AT_CHECK([ovs-appctl ovs/route/rule/show | grep -q 'lookup 42'])
+dnl Check that the route cache is unchanged (because the table has not been
+dnl created yet).
+AT_CHECK([ovs-appctl ovs/route/show table=42], [2], [], [stderr])
+AT_CHECK([tail -2 stderr], [0], [dnl
+Invalid param, table 'table=42' not found
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
+on_exit 'ip route flush table 42'
+AT_CHECK([ip route add 10.0.0.18/32 dev p1-route table 42])
+AT_CHECK([ip route add 10.0.0.19/32 dev p1-route table 42])
+AT_CHECK([ip route show table 42 | grep 'p1-route' | grep -q '10.0.0.18'])
+AT_CHECK([ip route show table 42 | grep 'p1-route' | grep -q '10.0.0.19'])
+
+AT_CHECK([ovs-appctl revalidator/wait])
+dnl Check that OVS learn those routes.
+AT_CHECK([ovs-appctl ovs/route/show table=42], [0], [dnl
+Route Table #42:
+Cached: 10.0.0.18/32 dev p1-route SRC 10.0.0.17
+Cached: 10.0.0.19/32 dev p1-route SRC 10.0.0.17
+])
+
+dnl Delete a route from the custom table and check that OVS removes the route
+dnl from the cache.
+AT_CHECK([ip route del 10.0.0.18/32 dev p1-route table 42])
+OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show table=42], [dnl
+Route Table #42:
+Cached: 10.0.0.19/32 dev p1-route SRC 10.0.0.17])
+
+dnl Delete the rule and check that the table no longer exists in the cache.
+AT_CHECK([ip rule del from 10.0.0.1 lookup 42])
+dnl Give the main thread a chance to act.
+AT_CHECK([ovs-appctl revalidator/wait])
+dnl Check that the cache is still the same.
+AT_CHECK([ovs-appctl ovs/route/show table=42], [2], [], [stderr])
+AT_CHECK([tail -2 stderr], [0], [dnl
+Invalid param, table 'table=42' not found
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
+dnl Add a custom table and check that OVS ignores it because no rule is
+dnl referencing it.
+on_exit 'ip route flush table 43'
+AT_CHECK([ip route add 10.0.0.18/32 dev p1-route table 43])
+AT_CHECK([ip route show table 43 | grep 'p1-route' | grep -q '10.0.0.18'])
+
+AT_CHECK([ovs-appctl revalidator/wait])
+dnl Check that the cache is still the same.
+AT_CHECK([ovs-appctl ovs/route/show table=43], [2], [], [stderr])
+AT_CHECK([tail -2 stderr], [0], [dnl
+Invalid param, table 'table=43' not found
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
-- 
2.50.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to