diff mbox series

[ovs-dev,6/8] ovs-router: Add 'table=ID' parameter in ovs/route/{add, del}.

Message ID 20250619125016.2660985-7-dchumak@nvidia.com
State Changes Requested
Delegated to: Ilya Maximets
Headers show
Series ovs-router: Multi-table routing infrastructure. | expand

Checks

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

Commit Message

Dima Chumak June 19, 2025, 12:50 p.m. UTC
Introduce an optional parameter to `ovs-appctl ovs/route/show` for
adding/deleting user routes in non-default routing tables.

The table ID must exist before adding/deleting a route, OVS will not
create a new table. It can be created by using kernel `ip route` tool.

Signed-off-by: Dima Chumak <dchumak@nvidia.com>
---
 NEWS                  |  2 ++
 lib/ovs-router.c      | 65 +++++++++++++++++++++++++++++++++++--------
 tests/system-route.at | 53 +++++++++++++++++++++++++++++++++++
 3 files changed, 108 insertions(+), 12 deletions(-)
diff mbox series

Patch

diff --git a/NEWS b/NEWS
index 99604d517ed7..b55a87d60100 100644
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,8 @@  Post-v3.5.0
      * '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/rule/show, to list OVS router rules.
+     * 'ovs/route/add' and 'ovs/route/del': added new option, table=ID, to
+       add/delete a route from a specific OVS table.
    - SSL/TLS:
      * Support for deprecated TLSv1 and TLSv1.1 protocols on OpenFlow and
        database connections is now removed.
diff --git a/lib/ovs-router.c b/lib/ovs-router.c
index 8e13292527d1..e0654a261a2a 100644
--- a/lib/ovs-router.c
+++ b/lib/ovs-router.c
@@ -488,7 +488,7 @@  rt_entry_delete__(const struct cls_rule *cr, struct classifier *cls)
 }
 
 static bool
-rt_entry_delete(uint32_t mark, uint8_t priority,
+rt_entry_delete(struct classifier *cls, uint32_t mark, uint8_t priority,
                 const struct in6_addr *ip6_dst, uint8_t plen)
 {
     const struct cls_rule *cr;
@@ -501,10 +501,10 @@  rt_entry_delete(uint32_t mark, uint8_t priority,
     cls_rule_init(&rule, &match, priority);
 
     /* Find the exact rule. */
-    cr = classifier_find_rule_exactly(&default_cls, &rule, OVS_VERSION_MAX);
+    cr = classifier_find_rule_exactly(cls, &rule, OVS_VERSION_MAX);
     if (cr) {
         ovs_mutex_lock(&mutex);
-        rt_entry_delete__(cr, &default_cls);
+        rt_entry_delete__(cr, cls);
         ovs_mutex_unlock(&mutex);
 
         res = true;
@@ -543,6 +543,7 @@  ovs_router_add(struct unixctl_conn *conn, int argc,
     struct in6_addr src6 = in6addr_any;
     struct in6_addr gw6 = in6addr_any;
     char src6_s[IPV6_SCAN_LEN + 1];
+    uint32_t table = CLS_DEFAULT;
     struct in6_addr ip6;
     uint32_t mark = 0;
     unsigned int plen;
@@ -588,6 +589,23 @@  ovs_router_add(struct unixctl_conn *conn, int argc,
             }
         }
 
+        if (ovs_scan(argv[i], "table=%"SCNi32, &table)) {
+            struct classifier *cls = cls_find(table);
+
+            if (!cls) {
+                struct ds ds = DS_EMPTY_INITIALIZER;
+
+                ds_put_format(&ds, "Table %s not found", argv[i]);
+                unixctl_command_reply_error(conn, ds_cstr_ro(&ds));
+                ds_destroy(&ds);
+                return;
+            }
+            continue;
+        } else if (ovs_scan(argv[i], "table=")) {
+            unixctl_command_reply_error(conn, "Invalid table format");
+            return;
+        }
+
         unixctl_command_reply_error(conn,
                                     "Invalid pkt_mark, IP gateway or src_ip");
         return;
@@ -600,7 +618,7 @@  ovs_router_add(struct unixctl_conn *conn, int argc,
         in6_addr_set_mapped_ipv4(&src6, src);
     }
 
-    err = ovs_router_insert__(CLS_DEFAULT, mark, plen + 32, false, &ip6, plen,
+    err = ovs_router_insert__(table, mark, plen + 32, false, &ip6, plen,
                               argv[2], &gw6, &src6);
     if (err) {
         unixctl_command_reply_error(conn, "Error while inserting route.");
@@ -613,10 +631,13 @@  static void
 ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
               const char *argv[], void *aux OVS_UNUSED)
 {
+    struct classifier *cls = &default_cls;
+    uint32_t table = CLS_DEFAULT;
     struct in6_addr ip6;
     uint32_t mark = 0;
     unsigned int plen;
     ovs_be32 ip;
+    int i;
 
     if (scan_ipv4_route(argv[1], &ip, &plen)) {
         in6_addr_set_mapped_ipv4(&ip6, ip);
@@ -625,14 +646,34 @@  ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
         unixctl_command_reply_error(conn, "Invalid parameters");
         return;
     }
-    if (argc > 2) {
-        if (!ovs_scan(argv[2], "pkt_mark=%"SCNi32, &mark)) {
-            unixctl_command_reply_error(conn, "Invalid pkt_mark");
+
+    /* Parse optional parameters. */
+    for (i = 2; i < argc; i++) {
+        if (ovs_scan(argv[i], "pkt_mark=%"SCNi32, &mark)) {
+            continue;
+        }
+
+        if (ovs_scan(argv[i], "table=%"SCNi32, &table)) {
+            cls = cls_find(table);
+            if (!cls) {
+                struct ds ds = DS_EMPTY_INITIALIZER;
+
+                ds_put_format(&ds, "Table %s not found", argv[i]);
+                unixctl_command_reply_error(conn, ds_cstr_ro(&ds));
+                ds_destroy(&ds);
+                return;
+            }
+            continue;
+        } else if (ovs_scan(argv[i], "table=")) {
+            unixctl_command_reply_error(conn, "Invalid table format");
             return;
         }
+
+        unixctl_command_reply_error(conn, "Invalid pkt_mark or table");
+        return;
     }
 
-    if (rt_entry_delete(mark, plen + 32, &ip6, plen)) {
+    if (rt_entry_delete(cls, mark, plen + 32, &ip6, plen)) {
         unixctl_command_reply(conn, "OK");
         seq_change(tnl_conf_seq);
     } else {
@@ -1028,13 +1069,13 @@  ovs_router_init(void)
         fatal_signal_add_hook(ovs_router_flush_handler, NULL, NULL, true);
         unixctl_command_register("ovs/route/add",
                                  "ip/plen dev [gw] "
-                                 "[pkt_mark=mark] [src=src_ip]",
-                                 2, 5, ovs_router_add, NULL);
+                                 "[pkt_mark=mark] [src=src_ip] [table=id]",
+                                 2, 6, ovs_router_add, NULL);
         unixctl_command_register("ovs/route/show", "[table=ID|all]", 0, 1,
                                  ovs_router_show, NULL);
         unixctl_command_register("ovs/route/del", "ip/plen "
-                                 "[pkt_mark=mark]", 1, 2, ovs_router_del,
-                                 NULL);
+                                 "[pkt_mark=mark] [table=id]", 1, 3,
+                                 ovs_router_del, NULL);
         unixctl_command_register("ovs/route/lookup", "ip_addr "
                                  "[pkt_mark=mark]", 1, 2,
                                  ovs_router_lookup_cmd, NULL);
diff --git a/tests/system-route.at b/tests/system-route.at
index 66bfd0e8ed2e..303178bfc4e3 100644
--- a/tests/system-route.at
+++ b/tests/system-route.at
@@ -332,3 +332,56 @@  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.
+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([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 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
+])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP