@@ -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,
@@ -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;
@@ -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])
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(-)