Add route-table support for ipv4 dst via ipv6. One use case is BGP unnumbered, a mechanism that establishes peering sessions without the need to explicitly configure IPv4 addresses on the interfaces involved in the peering. Without using IPv4 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 <w...@nvidia.com> --- v2: fix CI errors RFC: wait for Derrick to verify vxlan use case --- lib/ovs-router.c | 34 ++++++++++++++++------------------ lib/route-table.c | 21 +++++++++++++++++++++ tests/ovs-router.at | 33 +++++++++++++++++++++++++++++++++ tests/system-route.at | 24 ++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 18 deletions(-) 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..58412711888f 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 }, }; @@ -333,6 +335,25 @@ 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; + ovs_be32 gw; + + via = nl_attr_get(attrs[RTA_VIA]); + switch (via->rtvia_family) { + case AF_INET: + gw = *(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/ovs-router.at b/tests/ovs-router.at index b3314b3dff0d..f34dd3c99f5d 100644 --- a/tests/ovs-router.at +++ b/tests/ovs-router.at @@ -109,6 +109,39 @@ User: 2001:db8:beef::14/128 MARK 14 dev br0 GW 2001:db8:cafe::1 SRC 2001:db8:caf OVS_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([appctl - route/add with ipv4 via ipv6]) +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 +]) + +# defualt pick the first IP, 192.168.9.2, as src +AT_CHECK([ovs-appctl ovs/route/add 192.168.9.10/32 br0 fe80::920a:84ff:beef:9510], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 192.168.9.11/32 br0 fe80::920a:84ff:beef:9511 src=192.168.9.2], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 192.168.9.12/32 br0 fe80::920a:84ff:beef:9512 src=192.168.9.3], [0], [OK +]) +AT_CHECK([ovs-appctl ovs/route/add 192.168.9.13/32 br0 fe80::920a:84ff:beef:9513 pkt_mark=13 src=192.168.9.3], [0], [OK +]) + +AT_CHECK([ovs-appctl ovs/route/show | grep User | grep beef | sort], [0], [dnl +User: 192.168.9.10/32 dev br0 GW fe80::920a:84ff:beef:9510 SRC 192.168.9.2 +User: 192.168.9.11/32 dev br0 GW fe80::920a:84ff:beef:9511 SRC 192.168.9.2 +User: 192.168.9.12/32 dev br0 GW fe80::920a:84ff:beef:9512 SRC 192.168.9.3 +User: 192.168.9.13/32 MARK 13 dev br0 GW fe80::920a:84ff:beef:9513 SRC 192.168.9.3 +]) + +# DST and SRC must be in the same subnet domain +AT_CHECK([ovs-appctl ovs/route/add 192.168.10.12/32 br0 fe80::920a:84ff:beef:9512 src=192.168.9.3], [2], [], [dnl +Error while inserting route. +ovs-appctl: ovs-vswitchd: server returned an error +]) +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]) 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]) -- 2.37.1 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev