diff mbox series

[ovs-dev,RFC] route-table: Add support for v4 via v6 route.

Message ID 20240426231150.8029-1-witu@nvidia.com
State Superseded
Headers show
Series [ovs-dev,RFC] route-table: Add support for v4 via v6 route. | expand

Commit Message

William Tu April 26, 2024, 11:11 p.m. UTC
Add route-table that support ipv4 dst via ipv6. BGP unnumbered is mechanism
that allows BGP to establish peering sessions without the need to explicitly
configure IP addresses on the interfaces involved in the peering. Without
using IP address assignments, it uses link-local IPv6 addresses of the
directly connected neighbors for peering purposes. For example, BGP
might install the following route:
$ ip route get 100.87.18.3
 100.87.18.3 via inet6 fe80::920a:84ff:fe9e:9570 dev br-phy src 100.87.18.6

Currently OVS can only support either all-ipv4 or all-ipv6, the patch
adds support for such use case.

Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2024-January/052908.html
Signed-off-by: William Tu <witu@nvidia.com>
---
Need Derrick to verify it's working on tunnel
---
 lib/ovs-router.c      | 34 ++++++++++++++++------------------
 lib/route-table.c     | 26 ++++++++++++++++++++++++++
 tests/system-route.at | 24 ++++++++++++++++++++++++
 3 files changed, 66 insertions(+), 18 deletions(-)
diff mbox series

Patch

diff --git a/lib/ovs-router.c b/lib/ovs-router.c
index 3d84c9a30a8f..3ee927e6f7de 100644
--- a/lib/ovs-router.c
+++ b/lib/ovs-router.c
@@ -415,7 +415,6 @@  ovs_router_add(struct unixctl_conn *conn, int argc,
     unsigned int plen;
     ovs_be32 src = 0;
     ovs_be32 gw = 0;
-    bool is_ipv6;
     ovs_be32 ip;
     int err;
     int i;
@@ -423,9 +422,8 @@  ovs_router_add(struct unixctl_conn *conn, int argc,
     if (scan_ipv4_route(argv[1], &ip, &plen)) {
         in6_addr_set_mapped_ipv4(&ip6, ip);
         plen += 96;
-        is_ipv6 = false;
     } else if (scan_ipv6_route(argv[1], &ip6, &plen)) {
-        is_ipv6 = true;
+        ;
     } else {
         unixctl_command_reply_error(conn,
                                     "Invalid 'ip/plen' parameter");
@@ -438,21 +436,21 @@  ovs_router_add(struct unixctl_conn *conn, int argc,
             continue;
         }
 
-        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;
-            }
+        if (ovs_scan(argv[i], "src="IPV6_SCAN_FMT, src6_s) &&
+            ipv6_parse(src6_s, &src6)) {
+            continue;
+        }
+
+        if (ipv6_parse(argv[i], &gw6)) {
+            continue;
+        }
+
+        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,
diff --git a/lib/route-table.c b/lib/route-table.c
index f1fe32714e8d..7fa79edb5cda 100644
--- a/lib/route-table.c
+++ b/lib/route-table.c
@@ -232,6 +232,7 @@  route_table_parse(struct ofpbuf *buf, struct route_table_msg *change)
         [RTA_OIF] = { .type = NL_A_U32, .optional = true },
         [RTA_GATEWAY] = { .type = NL_A_U32, .optional = true },
         [RTA_MARK] = { .type = NL_A_U32, .optional = true },
+        [RTA_VIA] = { .type = NL_A_UNSPEC, .optional = true },
         [RTA_PREFSRC] = { .type = NL_A_U32, .optional = true },
         [RTA_TABLE] = { .type = NL_A_U32, .optional = true },
     };
@@ -241,6 +242,7 @@  route_table_parse(struct ofpbuf *buf, struct route_table_msg *change)
         [RTA_OIF] = { .type = NL_A_U32, .optional = true },
         [RTA_MARK] = { .type = NL_A_U32, .optional = true },
         [RTA_GATEWAY] = { .type = NL_A_IPV6, .optional = true },
+        [RTA_VIA] = { .type = NL_A_UNSPEC, .optional = true },
         [RTA_PREFSRC] = { .type = NL_A_IPV6, .optional = true },
         [RTA_TABLE] = { .type = NL_A_U32, .optional = true },
     };
@@ -323,6 +325,7 @@  route_table_parse(struct ofpbuf *buf, struct route_table_msg *change)
         } else if (ipv4) {
             in6_addr_set_mapped_ipv4(&change->rd.rta_dst, 0);
         }
+
         if (attrs[RTA_PREFSRC]) {
             if (ipv4) {
                 ovs_be32 prefsrc;
@@ -333,6 +336,29 @@  route_table_parse(struct ofpbuf *buf, struct route_table_msg *change)
                     nl_attr_get_in6_addr(attrs[RTA_PREFSRC]);
             }
         }
+
+        if (attrs[RTA_VIA]) {
+                const struct rtvia *via;
+
+                via = nl_attr_get(attrs[RTA_VIA]);
+                switch (via->rtvia_family) {
+                case AF_INET:
+                    ovs_be32 gw;
+                    gw = get_unaligned_be32(ALIGNED_CAST(ovs_be32 *,
+                                                         via->rtvia_addr));
+                    in6_addr_set_mapped_ipv4(&change->rd.rta_gw, gw);
+                    break;
+                case AF_INET6:
+                    change->rd.rta_gw = *(ALIGNED_CAST(struct in6_addr *,
+                                                       via->rtvia_addr));
+                    break;
+                default:
+                    VLOG_WARN("Unknown address family %d\n",
+                              via->rtvia_family);
+                    return 0;
+                }
+        }
+
         if (attrs[RTA_GATEWAY]) {
             if (ipv4) {
                 ovs_be32 gw;
diff --git a/tests/system-route.at b/tests/system-route.at
index c0ecad6cfb49..fd698321b401 100644
--- a/tests/system-route.at
+++ b/tests/system-route.at
@@ -65,6 +65,30 @@  Cached: fc00:db8:beef::13/128 dev br0 GW fc00:db8:cafe::1 SRC fc00:db8:cafe::2])
 OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ovs-route - add system route ipv4 via ipv6])
+AT_KEYWORDS([route])
+OVS_TRAFFIC_VSWITCHD_START()
+AT_CHECK([ip link set br0 up])
+
+AT_CHECK([ip addr add 192.168.9.2/24 dev br0], [0], [stdout])
+AT_CHECK([ip addr add 192.168.9.3/24 dev br0], [0], [stdout])
+
+AT_CHECK([ip -6 addr add fc00:db8:cafe::2/64 dev br0], [0], [stdout])
+AT_CHECK([ip -6 addr add fc00:db8:cafe::3/64 dev br0], [0], [stdout])
+
+AT_CHECK([ip route add 192.168.9.12/32 dev br0 via inet6 fe80::920a:84ff:fe9e:9512 src 192.168.9.2], [0], [stdout])
+AT_CHECK([ip route add 192.168.9.13/32 dev br0 via inet6 fe80::920a:84ff:fe9e:9513 src 192.168.9.3], [0], [stdout])
+
+OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep -E '192.168.9.1[[23]]/32' | sort], [dnl
+Cached: 192.168.9.12/32 dev br0 GW fe80::920a:84ff:fe9e:9512 SRC 192.168.9.2
+Cached: 192.168.9.13/32 dev br0 GW fe80::920a:84ff:fe9e:9513 SRC 192.168.9.3])
+
+AT_CHECK([ovs-appctl ovs/route/add 192.168.9.14/32 br0 fe80::920a:84ff:fe9e:9514], [0], [OK
+])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
+
 dnl Checks that OVS doesn't use routes from non-standard tables.
 AT_SETUP([ovs-route - route tables])
 AT_KEYWORDS([route])