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])