When adding a route with ovs/route/add command, the source address
in "ovs_router_entry" structure is always the FIRST address that the
interface has. See "ovs_router_get_netdev_source_address"
function for more information.

If an interface has multiple ipv4 and/or ipv6 addresses, there are use
cases where the user wants to control the source address. This patch
therefore addresses this issue by adding a src parameter.

Note that same constraints also exist when caching routes from
Kernel FIB with Netlink, but are not dealt with in this patch.

Acked-by: Eelco Chaudron <[email protected]>
Reviewed-by: Simon Horman <[email protected]>
Signed-off-by: Nobuhiro MIKI <[email protected]>
---
 NEWS                            |  3 ++
 lib/ovs-router.c                | 86 +++++++++++++++++++++++++++++----
 ofproto/ofproto-tnl-unixctl.man |  5 +-
 tests/ovs-router.at             | 80 +++++++++++++++++++++++++++++-
 4 files changed, 161 insertions(+), 13 deletions(-)

diff --git a/NEWS b/NEWS
index ad84898ce80f..2bae48d60829 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,9 @@ Post-v3.1.0
        in order to create OVSDB sockets with access mode of 0770.
    - QoS:
      * Added new configuration option 'jitter' for a linux-netem QoS type.
+   - ovs-appctl:
+     * Add support for selecting the source address with the
+       “ovs-appctl ovs/route/add" command.
 
 
 v3.1.0 - 16 Feb 2023
diff --git a/lib/ovs-router.c b/lib/ovs-router.c
index a47c2b9bd3fd..5255049c3a2f 100644
--- a/lib/ovs-router.c
+++ b/lib/ovs-router.c
@@ -164,6 +164,46 @@ static void rt_init_match(struct match *match, uint32_t 
mark,
     match->flow.pkt_mark = mark;
 }
 
+static int
+verify_prefsrc(const struct in6_addr *ip6_dst,
+               const char output_bridge[],
+               struct in6_addr *prefsrc)
+{
+    struct in6_addr *mask, *addr6;
+    struct netdev *dev;
+    int err, n_in6, i;
+
+    err = netdev_open(output_bridge, NULL, &dev);
+    if (err) {
+        return err;
+    }
+
+    err = netdev_get_addr_list(dev, &addr6, &mask, &n_in6);
+    if (err) {
+        goto out;
+    }
+
+    for (i = 0; i < n_in6; i++) {
+        struct in6_addr a1, a2;
+        a1 = ipv6_addr_bitand(ip6_dst, &mask[i]);
+        a2 = ipv6_addr_bitand(prefsrc, &mask[i]);
+
+        /* Check that the intarface has "prefsrc" and
+         * it is same broadcast domain with "ip6_dst". */
+        if (IN6_ARE_ADDR_EQUAL(prefsrc, &addr6[i]) &&
+            IN6_ARE_ADDR_EQUAL(&a1, &a2)) {
+            goto out;
+        }
+    }
+    err = ENOENT;
+
+out:
+    free(addr6);
+    free(mask);
+    netdev_close(dev);
+    return err;
+}
+
 int
 ovs_router_get_netdev_source_address(const struct in6_addr *ip6_dst,
                                      const char output_bridge[],
@@ -217,8 +257,12 @@ static int
 ovs_router_insert__(uint32_t mark, uint8_t priority, bool local,
                     const struct in6_addr *ip6_dst,
                     uint8_t plen, const char output_bridge[],
-                    const struct in6_addr *gw)
+                    const struct in6_addr *gw,
+                    const struct in6_addr *ip6_src)
 {
+    int (*get_src_addr)(const struct in6_addr *ip6_dst,
+                        const char output_bridge[],
+                        struct in6_addr *prefsrc);
     const struct cls_rule *cr;
     struct ovs_router_entry *p;
     struct match match;
@@ -236,11 +280,17 @@ ovs_router_insert__(uint32_t mark, uint8_t priority, bool 
local,
     p->plen = plen;
     p->local = local;
     p->priority = priority;
-    err = ovs_router_get_netdev_source_address(ip6_dst, output_bridge,
-                                               &p->src_addr);
+
+    if (ipv6_addr_is_set(ip6_src)) {
+        p->src_addr = *ip6_src;
+        get_src_addr = verify_prefsrc;
+    } else {
+        get_src_addr = ovs_router_get_netdev_source_address;
+    }
+
+    err = get_src_addr(ip6_dst, output_bridge, &p->src_addr);
     if (err && ipv6_addr_is_set(gw)) {
-        err = ovs_router_get_netdev_source_address(gw, output_bridge,
-                                                   &p->src_addr);
+        err = get_src_addr(gw, output_bridge, &p->src_addr);
     }
     if (err) {
         struct ds ds = DS_EMPTY_INITIALIZER;
@@ -274,7 +324,8 @@ ovs_router_insert(uint32_t mark, const struct in6_addr 
*ip_dst, uint8_t plen,
 {
     if (use_system_routing_table) {
         uint8_t priority = local ? plen + 64 : plen;
-        ovs_router_insert__(mark, priority, local, ip_dst, plen, 
output_bridge, gw);
+        ovs_router_insert__(mark, priority, local, ip_dst, plen,
+                            output_bridge, gw, &in6addr_any);
     }
 }
 
@@ -341,10 +392,13 @@ static void
 ovs_router_add(struct unixctl_conn *conn, int argc,
               const char *argv[], void *aux OVS_UNUSED)
 {
+    struct in6_addr src6 = in6addr_any;
     struct in6_addr gw6 = in6addr_any;
+    char src6_s[IPV6_SCAN_LEN + 1];
     struct in6_addr ip6;
     uint32_t mark = 0;
     unsigned int plen;
+    ovs_be32 src = 0;
     ovs_be32 gw = 0;
     bool is_ipv6;
     ovs_be32 ip;
@@ -370,24 +424,36 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
         }
 
         if (is_ipv6) {
+            if (ovs_scan(argv[i], "src="IPV6_SCAN_FMT, src6_s) &&
+                ipv6_parse(src6_s, &src6)) {
+                continue;
+            }
             if (ipv6_parse(argv[i], &gw6)) {
                 continue;
             }
         } else {
+            if (ovs_scan(argv[i], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src))) {
+                continue;
+            }
             if (ip_parse(argv[i], &gw)) {
                 continue;
             }
         }
 
-        unixctl_command_reply_error(conn, "Invalid pkt_mark or IP gateway");
+        unixctl_command_reply_error(conn,
+                                    "Invalid pkt_mark or IP gateway or src");
         return;
     }
 
     if (gw) {
         in6_addr_set_mapped_ipv4(&gw6, gw);
     }
+    if (src) {
+        in6_addr_set_mapped_ipv4(&src6, src);
+    }
 
-    err = ovs_router_insert__(mark, plen + 32, false, &ip6, plen, argv[2], 
&gw6);
+    err = ovs_router_insert__(mark, plen + 32, false, &ip6, plen, argv[2],
+                              &gw6, &src6);
     if (err) {
         unixctl_command_reply_error(conn, "Error while inserting route.");
     } else {
@@ -538,8 +604,8 @@ ovs_router_init(void)
         classifier_init(&cls, NULL);
         unixctl_command_register("ovs/route/add",
                                  "ip/plen output_bridge [gw] "
-                                 "[pkt_mark=mark]",
-                                 2, 4, ovs_router_add, NULL);
+                                 "[pkt_mark=mark] [src=src_ip]",
+                                 2, 5, ovs_router_add, NULL);
         unixctl_command_register("ovs/route/show", "", 0, 0,
                                  ovs_router_show, NULL);
         unixctl_command_register("ovs/route/del", "ip/plen "
diff --git a/ofproto/ofproto-tnl-unixctl.man b/ofproto/ofproto-tnl-unixctl.man
index 6ed7e7fcea9b..a801cfdccc5c 100644
--- a/ofproto/ofproto-tnl-unixctl.man
+++ b/ofproto/ofproto-tnl-unixctl.man
@@ -1,8 +1,9 @@
 .SS "OPENVSWITCH TUNNELING COMMANDS"
 These commands query and modify OVS tunnel components.
 .
-.IP "\fBovs/route/add ip/plen output_bridge [gw] [pkt_mark=mark]\fR"
-Adds ip/plen route to vswitchd routing table. output_bridge
+.IP "\fBovs/route/add \fIip\fB/\fIplen\fB \fIoutput_bridge\fB \
+[\fIgw\fB] [pkt_mark=\fImark\fB] [src=\fIsrc_ip\fB]\fR"
+Adds \fIip\fR/\fIplen\fR route to vswitchd routing table. \fIoutput_bridge\fR
 needs to be OVS bridge name.  This command is useful if OVS cached
 routes does not look right.
 .
diff --git a/tests/ovs-router.at b/tests/ovs-router.at
index a36990f1ea1d..1fa9d3f3c391 100644
--- a/tests/ovs-router.at
+++ b/tests/ovs-router.at
@@ -20,7 +20,7 @@ Invalid 'ip_addr/prefix_len' parameter
 ovs-appctl: ovs-vswitchd: server returned an error
 ])
 AT_CHECK([ovs-appctl ovs/route/add 2.2.2.4/24 br0 pkt_mark=baz], [2], [], [dnl
-Invalid pkt_mark or IP gateway
+Invalid pkt_mark or IP gateway or src
 ovs-appctl: ovs-vswitchd: server returned an error
 ])
 AT_CHECK([ovs-appctl ovs/route/show | grep User | sort], [0], [dnl
@@ -31,6 +31,84 @@ User: 2.2.2.3/32 MARK 1 dev br0 SRC 2.2.2.2
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([appctl - route/add with src - ipv4])
+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.168.9.2/24], [0], [OK
+])
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.168.9.3/24], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 192.168.9.11/32 br0 src=192.168.9.3], [0], 
[OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 192.168.10.12/32 br0 192.168.9.1 
src=192.168.9.3], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 192.168.10.13/32 br0 192.168.9.1 
pkt_mark=13 src=192.168.9.3], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 192.168.10.14/32 br0 192.168.9.1 
pkt_mark=14 src=192.168.9.2], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 192.168.10.15/32 br0 192.168.9.1 
src=foo.bar.9.200], [2], [], [dnl
+Invalid pkt_mark or IP gateway or src
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+AT_CHECK([ovs-appctl ovs/route/add 192.168.10.16/32 br0 192.168.9.1 
src=192.168.9.200], [2], [], [dnl
+Error while inserting route.
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+AT_CHECK([ovs-appctl ovs/route/add 192.168.10.17/32 br0 192.168.11.1 
src=192.168.9.3], [2], [], [dnl
+Error while inserting route.
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+AT_CHECK([ovs-appctl ovs/route/add 192.168.10.18/32 br0 src=192.168.9.3], [2], 
[], [dnl
+Error while inserting route.
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+AT_CHECK([ovs-appctl ovs/route/show | grep User | grep 192.168.10 | sort], 
[0], [dnl
+User: 192.168.10.12/32 dev br0 GW 192.168.9.1 SRC 192.168.9.3
+User: 192.168.10.13/32 MARK 13 dev br0 GW 192.168.9.1 SRC 192.168.9.3
+User: 192.168.10.14/32 MARK 14 dev br0 GW 192.168.9.1 SRC 192.168.9.2
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([appctl - route/add with src - ipv6])
+AT_KEYWORDS([ovs_router])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy])
+AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:db8:cafe::2/64], [0], [OK
+])
+AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:db8:cafe::3/64], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 2001:db8:cafe::11/128 br0 
src=2001:db8:cafe::3], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::12/128 br0 2001:db8:cafe::1 
src=2001:db8:cafe::3], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::13/128 br0 2001:db8:cafe::1 
pkt_mark=13 src=2001:db8:cafe::3], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::14/128 br0 2001:db8:cafe::1 
pkt_mark=14 src=2001:db8:cafe::2], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::15/128 br0 2001:db8:cafe::1 
src=foo:bar:2001:db8:cafe], [2], [], [dnl
+Invalid pkt_mark or IP gateway or src
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::16/128 br0 2001:db8:cafe::1 
src=2001:db8:cafe::200], [2], [], [dnl
+Error while inserting route.
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::17/128 br0 2001:db8:face::1 
src=2001:db8:cafe::3], [2], [], [dnl
+Error while inserting route.
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::18/128 br0 
src=2001:db8:cafe::3], [2], [], [dnl
+Error while inserting route.
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+AT_CHECK([ovs-appctl ovs/route/show | grep User | grep 2001:db8:beef | sort], 
[0], [dnl
+User: 2001:db8:beef::12/128 dev br0 GW 2001:db8:cafe::1 SRC 2001:db8:cafe::3
+User: 2001:db8:beef::13/128 MARK 13 dev br0 GW 2001:db8:cafe::1 SRC 
2001:db8:cafe::3
+User: 2001:db8:beef::14/128 MARK 14 dev br0 GW 2001:db8:cafe::1 SRC 
2001:db8:cafe::2
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([appctl - route/lookup])
 AT_KEYWORDS([ovs_router])
 OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy])
-- 
2.31.1

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to