diff mbox series

[ovs-dev,v2,7/8] ovs-router: Add 'src=IP' parameter in ovs/route/lookup.

Message ID 20250723131253.3704827-8-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 July 23, 2025, 1:12 p.m. UTC
Introduce an optional parameter to `ovs-appctl ovs/route/lookup` for
matching on source IP address.

Signed-off-by: Dima Chumak <dchumak@nvidia.com>
---
 Documentation/howto/userspace-tunneling.rst |  2 +-
 NEWS                                        |  2 +
 lib/ovs-router.c                            | 47 ++++++++++++++----
 ofproto/ofproto-tnl-unixctl.man             |  5 ++
 tests/system-route.at                       | 54 +++++++++++++++++++++
 5 files changed, 99 insertions(+), 11 deletions(-)

Comments

Ilya Maximets Aug. 1, 2025, 8:44 p.m. UTC | #1
On 7/23/25 3:12 PM, Dima Chumak via dev wrote:
> Introduce an optional parameter to `ovs-appctl ovs/route/lookup` for
> matching on source IP address.
> 
> Signed-off-by: Dima Chumak <dchumak@nvidia.com>
> ---
>  Documentation/howto/userspace-tunneling.rst |  2 +-
>  NEWS                                        |  2 +
>  lib/ovs-router.c                            | 47 ++++++++++++++----
>  ofproto/ofproto-tnl-unixctl.man             |  5 ++
>  tests/system-route.at                       | 54 +++++++++++++++++++++
>  5 files changed, 99 insertions(+), 11 deletions(-)
> 
> diff --git a/Documentation/howto/userspace-tunneling.rst b/Documentation/howto/userspace-tunneling.rst
> index 1c7e551e0726..5bebbce69e4a 100644
> --- a/Documentation/howto/userspace-tunneling.rst
> +++ b/Documentation/howto/userspace-tunneling.rst
> @@ -217,7 +217,7 @@ To delete route::
>  
>  To look up and display the route for a destination::
>  
> -    $ ovs-appctl ovs/route/lookup <IP address>
> +    $ ovs-appctl ovs/route/lookup <IP address> [src=IP]
>  
>  ARP
>  ~~~
> diff --git a/NEWS b/NEWS
> index c8f4592a6e2f..a58baa0a8365 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -29,6 +29,8 @@ v3.6.0 - xx xxx xxxx
>       * Added a new sub-command, ovs/router/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.
> +     * 'ovs/route/lookup': added new option, src=IP, to perform lookup with
> +       a specific source IP address.
>     - 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 ba4c43ab1d2e..a3df5c6fc2cc 100644
> --- a/lib/ovs-router.c
> +++ b/lib/ovs-router.c
> @@ -978,30 +978,57 @@ static void
>  ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc,
>                        const char *argv[], void *aux OVS_UNUSED)
>  {
> -    struct in6_addr gw, src = in6addr_any;
> +    struct in6_addr gw, src6 = in6addr_any;
> +    char src6_s[IPV6_SCAN_LEN + 1];
>      char iface[IFNAMSIZ];
>      struct in6_addr ip6;
>      unsigned int plen;
>      uint32_t mark = 0;
> +    ovs_be32 src = 0;
> +    bool is_ipv6;
>      ovs_be32 ip;
> +    int i;
>  
>      if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) {
>          in6_addr_set_mapped_ipv4(&ip6, ip);
> -    } else if (!(scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128)) {
> -        unixctl_command_reply_error(conn, "Invalid parameters");
> +        is_ipv6 = false;
> +    } else if (scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128) {
> +        is_ipv6 = true;
> +    } else {
> +        unixctl_command_reply_error(conn, "Invalid 'ip/plen' parameter");
>          return;
>      }
> -    if (argc > 2) {
> -        if (!ovs_scan(argv[2], "pkt_mark=%"SCNi32, &mark)) {
> -            unixctl_command_reply_error(conn, "Invalid pkt_mark");
> -            return;
> +
> +    /* Parse optional parameters. */
> +    for (i = 2; i < argc; i++) {
> +        if (ovs_scan(argv[i], "pkt_mark=%"SCNi32, &mark)) {

SNCu32

> +            continue;
> +        }
> +
> +        if (is_ipv6) {
> +            if (ovs_scan(argv[i], "src="IPV6_SCAN_FMT, src6_s) &&
> +                ipv6_parse(src6_s, &src6)) {
> +                continue;
> +            }
> +        } else {
> +            if (ovs_scan(argv[i], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src))) {
> +                continue;
> +            }
>          }
> +
> +        unixctl_command_reply_error(conn, "Invalid pkt_mark or src");
> +        return;
> +    }
> +
> +    if (src) {
> +        in6_addr_set_mapped_ipv4(&src6, src);
>      }
> -    if (ovs_router_lookup(mark, &ip6, iface, &src, &gw)) {
> +
> +    if (ovs_router_lookup(mark, &ip6, iface, &src6, &gw)) {
>          struct ds ds = DS_EMPTY_INITIALIZER;
>  
>          ds_put_format(&ds, "src ");
> -        ipv6_format_mapped(&src, &ds);
> +        ipv6_format_mapped(&src6, &ds);
>          ds_put_format(&ds, "\ngateway ");
>          ipv6_format_mapped(&gw, &ds);
>          ds_put_format(&ds, "\ndev %s\n", iface);
> @@ -1150,7 +1177,7 @@ ovs_router_init(void)
>                                   "[pkt_mark=mark] [table=id]", 1, 3,
>                                   ovs_router_del, NULL);
>          unixctl_command_register("ovs/route/lookup", "ip_addr "
> -                                 "[pkt_mark=mark]", 1, 2,
> +                                 "[pkt_mark=mark] [src=src_ip]", 1, 3,
>                                   ovs_router_lookup_cmd, NULL);
>          unixctl_command_register("ovs/route/rule/show", "", 0, 0,
>                                   ovs_router_rules_show, NULL);
> diff --git a/ofproto/ofproto-tnl-unixctl.man b/ofproto/ofproto-tnl-unixctl.man
> index 654f405109e6..8e48c0e809ac 100644
> --- a/ofproto/ofproto-tnl-unixctl.man
> +++ b/ofproto/ofproto-tnl-unixctl.man
> @@ -21,6 +21,11 @@ Delete ip/plen route from OVS routing table. The standard routing table is used
>  by default, or a specific custom table when ID is provided via \fItable\fR
>  parameter.
>  .
> +.IP "\fBovs/route/lookup \fIip_addr\fB [pkt_mark=\fImark\fB] [src=\fIIP\fB]\fR"

Should likely be src=src_ip instead of src=IP, otherwise it's hard to tell
the arguments apart in the description.  Names are also inconsistent througout
the patch.

> +Perform route lookup for specified destination IP address in OVS routing tables
> +and print the matching route information. The lookup may be more selective when
> +additional parameters are used: \fIpkt_mark\fR and \fIsrc\fR.
> +.
>  .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.
> diff --git a/tests/system-route.at b/tests/system-route.at
> index 09b2861443cb..7efb50cfbc23 100644
> --- a/tests/system-route.at
> +++ b/tests/system-route.at
> @@ -412,3 +412,57 @@ ovs-appctl: ovs-vswitchd: server returned an error
>  
>  OVS_TRAFFIC_VSWITCHD_STOP
>  AT_CLEANUP
> +
> +dnl Checks that OVS performs route lookup according to rules with src match.
> +AT_SETUP([ovs-route - route lookup + rules])
> +AT_KEYWORDS([route])
> +OVS_TRAFFIC_VSWITCHD_START()
> +
> +dnl Create tap ports.
> +on_exit 'ip link del p1-route'
> +on_exit 'ip link del p2-route'
> +AT_CHECK([ip tuntap add name p1-route mode tap])
> +AT_CHECK([ip tuntap add name p2-route mode tap])
> +AT_CHECK([ip link set p1-route up])
> +AT_CHECK([ip link set p2-route up])
> +
> +dnl Add ip addresses, they need to be from same subnet so that the main router
> +dnl table in OVS contains only one of the routes.
> +AT_CHECK([ip addr add 10.0.0.11/24 dev p1-route], [0], [stdout])
> +AT_CHECK([ip addr add 10.0.0.12/24 dev p2-route], [0], [stdout])
> +dnl Give the main thread a chance to act.
> +AT_CHECK([ovs-appctl revalidator/wait])
> +dnl Check that OVS learn only one of the routes to the subnet.
> +AT_CHECK([test `ovs-appctl ovs/route/show | grep -F '10.0.0.0/24' | grep -c 'p1-route'` -eq 0])

Use 'grep -q' instead of the test with the [1] as an expected exit code.

> +AT_CHECK([ovs-appctl ovs/route/show | grep -F '10.0.0.0/24' | grep -q 'p2-route'])
> +dnl Check that OVS lookup returns p2-route even for srouce IP of p1-route
> +dnl internface.
> +AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.10 src=10.0.0.11], [0], [src 10.0.0.11
> +gateway ::
> +dev p2-route
> +])
> +
> +dnl Add rule to use a custom routing table for source IP of p1-route.
> +on_exit 'ip route flush table 42'
> +AT_CHECK([ip route add 10.0.0.0/24 dev p1-route table 42])
> +AT_CHECK([ip route show table 42 | grep 'p1-route' | grep -Fq '10.0.0.0/24'])
> +on_exit 'ip rule del from 10.0.0.11 lookup 42'
> +AT_CHECK([ip rule add from 10.0.0.11 lookup 42])
> +AT_CHECK([ip rule show | grep -q 'from 10.0.0.11 lookup 42'])
> +dnl Give the main thread a chance to act.
> +AT_CHECK([ovs-appctl revalidator/wait])
> +dnl Check that OVS uses custom table for lookup with source IP of p1-route
> +dnl internface and returns the correct p1-route.
> +AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.10 src=10.0.0.11], [0], [src 10.0.0.11
> +gateway ::
> +dev p1-route
> +])
> +dnl Check that OVS uses main table for lookup with other source IP that doesn't
> +dnl match the rule.
> +AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.10 src=10.0.0.12], [0], [src 10.0.0.12
> +gateway ::
> +dev p2-route
> +])
> +
> +OVS_TRAFFIC_VSWITCHD_STOP
> +AT_CLEANUP
diff mbox series

Patch

diff --git a/Documentation/howto/userspace-tunneling.rst b/Documentation/howto/userspace-tunneling.rst
index 1c7e551e0726..5bebbce69e4a 100644
--- a/Documentation/howto/userspace-tunneling.rst
+++ b/Documentation/howto/userspace-tunneling.rst
@@ -217,7 +217,7 @@  To delete route::
 
 To look up and display the route for a destination::
 
-    $ ovs-appctl ovs/route/lookup <IP address>
+    $ ovs-appctl ovs/route/lookup <IP address> [src=IP]
 
 ARP
 ~~~
diff --git a/NEWS b/NEWS
index c8f4592a6e2f..a58baa0a8365 100644
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,8 @@  v3.6.0 - xx xxx xxxx
      * Added a new sub-command, ovs/router/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.
+     * 'ovs/route/lookup': added new option, src=IP, to perform lookup with
+       a specific source IP address.
    - 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 ba4c43ab1d2e..a3df5c6fc2cc 100644
--- a/lib/ovs-router.c
+++ b/lib/ovs-router.c
@@ -978,30 +978,57 @@  static void
 ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc,
                       const char *argv[], void *aux OVS_UNUSED)
 {
-    struct in6_addr gw, src = in6addr_any;
+    struct in6_addr gw, src6 = in6addr_any;
+    char src6_s[IPV6_SCAN_LEN + 1];
     char iface[IFNAMSIZ];
     struct in6_addr ip6;
     unsigned int plen;
     uint32_t mark = 0;
+    ovs_be32 src = 0;
+    bool is_ipv6;
     ovs_be32 ip;
+    int i;
 
     if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) {
         in6_addr_set_mapped_ipv4(&ip6, ip);
-    } else if (!(scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128)) {
-        unixctl_command_reply_error(conn, "Invalid parameters");
+        is_ipv6 = false;
+    } else if (scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128) {
+        is_ipv6 = true;
+    } else {
+        unixctl_command_reply_error(conn, "Invalid 'ip/plen' parameter");
         return;
     }
-    if (argc > 2) {
-        if (!ovs_scan(argv[2], "pkt_mark=%"SCNi32, &mark)) {
-            unixctl_command_reply_error(conn, "Invalid pkt_mark");
-            return;
+
+    /* Parse optional parameters. */
+    for (i = 2; i < argc; i++) {
+        if (ovs_scan(argv[i], "pkt_mark=%"SCNi32, &mark)) {
+            continue;
+        }
+
+        if (is_ipv6) {
+            if (ovs_scan(argv[i], "src="IPV6_SCAN_FMT, src6_s) &&
+                ipv6_parse(src6_s, &src6)) {
+                continue;
+            }
+        } else {
+            if (ovs_scan(argv[i], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src))) {
+                continue;
+            }
         }
+
+        unixctl_command_reply_error(conn, "Invalid pkt_mark or src");
+        return;
+    }
+
+    if (src) {
+        in6_addr_set_mapped_ipv4(&src6, src);
     }
-    if (ovs_router_lookup(mark, &ip6, iface, &src, &gw)) {
+
+    if (ovs_router_lookup(mark, &ip6, iface, &src6, &gw)) {
         struct ds ds = DS_EMPTY_INITIALIZER;
 
         ds_put_format(&ds, "src ");
-        ipv6_format_mapped(&src, &ds);
+        ipv6_format_mapped(&src6, &ds);
         ds_put_format(&ds, "\ngateway ");
         ipv6_format_mapped(&gw, &ds);
         ds_put_format(&ds, "\ndev %s\n", iface);
@@ -1150,7 +1177,7 @@  ovs_router_init(void)
                                  "[pkt_mark=mark] [table=id]", 1, 3,
                                  ovs_router_del, NULL);
         unixctl_command_register("ovs/route/lookup", "ip_addr "
-                                 "[pkt_mark=mark]", 1, 2,
+                                 "[pkt_mark=mark] [src=src_ip]", 1, 3,
                                  ovs_router_lookup_cmd, NULL);
         unixctl_command_register("ovs/route/rule/show", "", 0, 0,
                                  ovs_router_rules_show, NULL);
diff --git a/ofproto/ofproto-tnl-unixctl.man b/ofproto/ofproto-tnl-unixctl.man
index 654f405109e6..8e48c0e809ac 100644
--- a/ofproto/ofproto-tnl-unixctl.man
+++ b/ofproto/ofproto-tnl-unixctl.man
@@ -21,6 +21,11 @@  Delete ip/plen route from OVS routing table. The standard routing table is used
 by default, or a specific custom table when ID is provided via \fItable\fR
 parameter.
 .
+.IP "\fBovs/route/lookup \fIip_addr\fB [pkt_mark=\fImark\fB] [src=\fIIP\fB]\fR"
+Perform route lookup for specified destination IP address in OVS routing tables
+and print the matching route information. The lookup may be more selective when
+additional parameters are used: \fIpkt_mark\fR and \fIsrc\fR.
+.
 .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.
diff --git a/tests/system-route.at b/tests/system-route.at
index 09b2861443cb..7efb50cfbc23 100644
--- a/tests/system-route.at
+++ b/tests/system-route.at
@@ -412,3 +412,57 @@  ovs-appctl: ovs-vswitchd: server returned an error
 
 OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
+
+dnl Checks that OVS performs route lookup according to rules with src match.
+AT_SETUP([ovs-route - route lookup + rules])
+AT_KEYWORDS([route])
+OVS_TRAFFIC_VSWITCHD_START()
+
+dnl Create tap ports.
+on_exit 'ip link del p1-route'
+on_exit 'ip link del p2-route'
+AT_CHECK([ip tuntap add name p1-route mode tap])
+AT_CHECK([ip tuntap add name p2-route mode tap])
+AT_CHECK([ip link set p1-route up])
+AT_CHECK([ip link set p2-route up])
+
+dnl Add ip addresses, they need to be from same subnet so that the main router
+dnl table in OVS contains only one of the routes.
+AT_CHECK([ip addr add 10.0.0.11/24 dev p1-route], [0], [stdout])
+AT_CHECK([ip addr add 10.0.0.12/24 dev p2-route], [0], [stdout])
+dnl Give the main thread a chance to act.
+AT_CHECK([ovs-appctl revalidator/wait])
+dnl Check that OVS learn only one of the routes to the subnet.
+AT_CHECK([test `ovs-appctl ovs/route/show | grep -F '10.0.0.0/24' | grep -c 'p1-route'` -eq 0])
+AT_CHECK([ovs-appctl ovs/route/show | grep -F '10.0.0.0/24' | grep -q 'p2-route'])
+dnl Check that OVS lookup returns p2-route even for srouce IP of p1-route
+dnl internface.
+AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.10 src=10.0.0.11], [0], [src 10.0.0.11
+gateway ::
+dev p2-route
+])
+
+dnl Add rule to use a custom routing table for source IP of p1-route.
+on_exit 'ip route flush table 42'
+AT_CHECK([ip route add 10.0.0.0/24 dev p1-route table 42])
+AT_CHECK([ip route show table 42 | grep 'p1-route' | grep -Fq '10.0.0.0/24'])
+on_exit 'ip rule del from 10.0.0.11 lookup 42'
+AT_CHECK([ip rule add from 10.0.0.11 lookup 42])
+AT_CHECK([ip rule show | grep -q 'from 10.0.0.11 lookup 42'])
+dnl Give the main thread a chance to act.
+AT_CHECK([ovs-appctl revalidator/wait])
+dnl Check that OVS uses custom table for lookup with source IP of p1-route
+dnl internface and returns the correct p1-route.
+AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.10 src=10.0.0.11], [0], [src 10.0.0.11
+gateway ::
+dev p1-route
+])
+dnl Check that OVS uses main table for lookup with other source IP that doesn't
+dnl match the rule.
+AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.10 src=10.0.0.12], [0], [src 10.0.0.12
+gateway ::
+dev p2-route
+])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP