Skip to content

Commit

Permalink
route-table: Support parsing multipath routes.
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Felix Huettner <[email protected]>
Signed-off-by: Frode Nordahl <[email protected]>
Signed-off-by: 0-day Robot <[email protected]>
  • Loading branch information
2 people authored and ovsrobot committed Dec 6, 2024
1 parent f044c28 commit 38ebd4e
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 12 deletions.
65 changes: 53 additions & 12 deletions lib/route-table.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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[] = {
Expand All @@ -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)];
Expand Down Expand Up @@ -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]) {
Expand Down Expand Up @@ -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");
Expand Down
72 changes: 72 additions & 0 deletions tests/system-route.at
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand All @@ -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])
Expand Down

0 comments on commit 38ebd4e

Please sign in to comment.