From 38ebd4e8e9b58557aa790445cbf8dec37aca1161 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Fri, 6 Dec 2024 21:26:37 +0100 Subject: [PATCH] route-table: Support parsing multipath routes. The route-table code is useful outside the scope of Open vSwitch. In a subsequent patch we will expose the route-table data structures to allow projects compiling against the private Open vSwitch library to consume this data. Add support for storing and parsing multipath routes. Note that the internal handling of routes in the ovs-route module does currently not support multipath routes, so when presented with one the latter occurrence will be stored. This is not a regression as these routes were previously not considered at all. Storing the information in the route-table module data structure will allow external to OVS projects make use of this data. Co-Authored-by: Felix Huettner Signed-off-by: Felix Huettner Signed-off-by: Frode Nordahl Signed-off-by: 0-day Robot --- lib/route-table.c | 65 ++++++++++++++++++++++++++++++-------- tests/system-route.at | 72 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 12 deletions(-) diff --git a/lib/route-table.c b/lib/route-table.c index f452d96fcee..4f0fed48543 100644 --- a/lib/route-table.c +++ b/lib/route-table.c @@ -256,6 +256,18 @@ route_table_reset(void) /* Return RTNLGRP_IPV4_ROUTE or RTNLGRP_IPV6_ROUTE on success, 0 on parse * error. */ +#define ROUTE_TABLE_PARSE_IF_INDEXTONAME(IFINDEX, IFNAME) \ + if (!if_indextoname(IFINDEX, IFNAME)) { \ + int error = errno; \ + \ + VLOG_DBG_RL(&rl, "Could not find interface name[%u]: %s", \ + IFINDEX, ovs_strerror(error)); \ + if (error == ENXIO) { \ + change->relevant = false; \ + } else { \ + goto error_out; \ + } \ + } static int route_table_parse__(struct ofpbuf *buf, size_t ofs, const struct nlmsghdr *nlmsg, @@ -274,6 +286,7 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, [RTA_TABLE] = { .type = NL_A_U32, .optional = true }, [RTA_PRIORITY] = { .type = NL_A_U32, .optional = true }, [RTA_VIA] = { .type = NL_A_UNSPEC, .optional = true }, + [RTA_MULTIPATH] = { .type = NL_A_NESTED, .optional = true }, }; static const struct nl_policy policy6[] = { @@ -285,6 +298,7 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, [RTA_TABLE] = { .type = NL_A_U32, .optional = true }, [RTA_PRIORITY] = { .type = NL_A_U32, .optional = true }, [RTA_VIA] = { .type = NL_A_UNSPEC, .optional = true }, + [RTA_MULTIPATH] = { .type = NL_A_NESTED, .optional = true }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; @@ -330,18 +344,7 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, if (attrs[RTA_OIF]) { rta_oif = nl_attr_get_u32(attrs[RTA_OIF]); - - if (!if_indextoname(rta_oif, rdnh->ifname)) { - int error = errno; - - VLOG_DBG_RL(&rl, "Could not find interface name[%u]: %s", - rta_oif, ovs_strerror(error)); - if (error == ENXIO) { - change->relevant = false; - } else { - goto error_out; - } - } + ROUTE_TABLE_PARSE_IF_INDEXTONAME(rta_oif, rdnh->ifname); } if (attrs[RTA_DST]) { @@ -398,6 +401,44 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, goto error_out; } } + if (attrs[RTA_MULTIPATH]) { + const struct nlattr *nla; + size_t left; + + NL_NESTED_FOR_EACH (nla, left, attrs[RTA_MULTIPATH]) { + struct route_table_msg mp_change; + struct rtnexthop *rtnh; + struct ofpbuf mp_buf; + int rc; + + route_data_init(&mp_change.rd); + ofpbuf_use_data(&mp_buf, nla, nla->nla_len); + rtnh = ofpbuf_try_pull(&mp_buf, sizeof *rtnh); + if (!rtnh) { + VLOG_DBG_RL(&rl, "Got short message while parsing " + "multipath attribute."); + goto error_out; + } + + rc = route_table_parse__(&mp_buf, 0, + nlmsg, rtm, &mp_change); + + if (rc == RTNLGRP_IPV4_ROUTE + || rc == RTNLGRP_IPV6_ROUTE) { + struct route_data_nexthop *mp_rdnh; + LIST_FOR_EACH (mp_rdnh, nexthop_node, + &mp_change.rd.nexthops) { + ROUTE_TABLE_PARSE_IF_INDEXTONAME(rtnh->rtnh_ifindex, + mp_rdnh->ifname); + } + ovs_list_push_back_all(&change->rd.nexthops, + &mp_change.rd.nexthops); + } else { + route_data_destroy(&mp_change.rd); + goto error_out; + } + } + } ovs_list_insert(&change->rd.nexthops, &rdnh->nexthop_node); } else { VLOG_DBG_RL(&rl, "received unparseable rtnetlink route message"); diff --git a/tests/system-route.at b/tests/system-route.at index 46db676f2cd..c50b8d8b7f4 100644 --- a/tests/system-route.at +++ b/tests/system-route.at @@ -44,6 +44,30 @@ Cached: 192.168.10.13/32 dev br0 GW 192.168.9.1 SRC 192.168.9.3]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([ovs-route - add system route with multiple nexthop - ipv4]) +AT_KEYWORDS([route]) +OVS_TRAFFIC_VSWITCHD_START() + +dnl Create tap ports. +AT_CHECK([ip tuntap add name p1-route mode tap]) +AT_CHECK([ip link set p1-route up]) +on_exit 'ip link del p1-route' +AT_CHECK([ip tuntap add name p2-route mode tap]) +AT_CHECK([ip link set p2-route up]) +on_exit 'ip link del p2-route' + +AT_CHECK([ip addr add 192.168.42.10/24 dev p1-route], [0], [stdout]) +AT_CHECK([ip addr add 192.168.51.10/24 dev p2-route], [0], [stdout]) +AT_CHECK([ip route add 172.16.42.0/24 nexthop via 192.168.42.1 dev p1-route nexthop via 192.168.51.1 dev p2-route], [0], [stdout]) + +dnl NOTE(fnordahl): At the time of this writing, it is expected that only the +dnl second route will be stored in ovs-router. +OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep -E '172.16.42.0/24' | sort], [dnl +Cached: 172.16.42.0/24 dev p2-route GW 192.168.51.1 SRC 192.168.51.10]) + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([ovs-route - add system route with src - ipv6]) AT_KEYWORDS([route]) OVS_TRAFFIC_VSWITCHD_START() @@ -65,6 +89,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 with multiple nexthop - ipv6]) +AT_KEYWORDS([route]) +OVS_TRAFFIC_VSWITCHD_START() + +dnl Create tap ports. +AT_CHECK([ip tuntap add name p1-route mode tap]) +AT_CHECK([ip link set p1-route up]) +on_exit 'ip link del p1-route' +AT_CHECK([ip tuntap add name p2-route mode tap]) +AT_CHECK([ip link set p2-route up]) +on_exit 'ip link del p2-route' + +AT_CHECK([ip -6 addr add fc00:db8:dead::10/64 dev p1-route], [0], [stdout]) +AT_CHECK([ip -6 addr add fc00:db8:beef::10/64 dev p2-route], [0], [stdout]) +AT_CHECK([ip -6 route add fc00:db8:cafe::/64 nexthop via fc00:db8:dead::1 dev p1-route nexthop via fc00:db8:beef::1 dev p2-route], [0], [stdout]) + +dnl NOTE(fnordahl): At the time of this writing, it is expected that only the +dnl second route will be stored in ovs-router. +OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep -E 'fc00:db8:cafe::/64' | sort], [dnl +Cached: fc00:db8:cafe::/64 dev p2-route GW fc00:db8:beef::1 SRC fc00:db8:beef::10]) + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([ovs-route - add system route - ipv4 via ipv6 nexthop]) AT_KEYWORDS([route]) OVS_TRAFFIC_VSWITCHD_START() @@ -83,6 +131,30 @@ Cached: 192.168.10.12/32 dev br0 GW fe80::253:ff:fe00:51 SRC fe80::253:ff:fe00:4 OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([ovs-route - add system route - ipv4 via multiple ipv6 nexthop]) +AT_KEYWORDS([route]) +OVS_TRAFFIC_VSWITCHD_START() + +dnl Create tap ports. +AT_CHECK([ip tuntap add name p1-route mode tap]) +AT_CHECK([ip link set p1-route up]) +on_exit 'ip link del p1-route' +AT_CHECK([ip tuntap add name p2-route mode tap]) +AT_CHECK([ip link set p2-route up]) +on_exit 'ip link del p2-route' + +AT_CHECK([ip -6 addr add fc00:db8:dead::10/64 dev p1-route], [0], [stdout]) +AT_CHECK([ip -6 addr add fc00:db8:beef::10/64 dev p2-route], [0], [stdout]) +AT_CHECK([ip route add 172.16.42.0/24 nexthop via inet6 fc00:db8:dead::1 dev p1-route nexthop via inet6 fc00:db8:beef::1 dev p2-route], [0], [stdout]) + +dnl NOTE(fnordahl): At the time of this writing, it is expected that only the +dnl second route will be stored in ovs-router. +OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep -E '172.16.42.0/24' | sort], [dnl +Cached: 172.16.42.0/24 dev p2-route GW fc00:db8:beef::1 SRC fc00:db8:beef::10]) + +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])