Skip to content

Commit

Permalink
route-table: Store nexthops in linked list.
Browse files Browse the repository at this point in the history
In preparation for adding support for parsing routes with multiple
next hops per destination, we need dynamic storage for them.

Storage for the primary next hop is set aside in struct route_data
to avoid unnecessary memory allocations for the common case of one
next hop.

The ovs-router module does not support storing multiple next hop
information, and the existing parser does not understand them, so
we retain this behavior by only passing single next hop routes for
storage in ovs-router.

This patch makes this change in isolation so that it is easier to
review subsequent patches.

Signed-off-by: Frode Nordahl <[email protected]>
Signed-off-by: 0-day Robot <[email protected]>
  • Loading branch information
fnordahl authored and ovsrobot committed Dec 18, 2024
1 parent 50009d5 commit f62cbb8
Showing 1 changed file with 81 additions and 13 deletions.
94 changes: 81 additions & 13 deletions lib/route-table.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "netlink.h"
#include "netlink-notifier.h"
#include "netlink-socket.h"
#include "openvswitch/list.h"
#include "openvswitch/ofpbuf.h"
#include "ovs-router.h"
#include "packets.h"
Expand All @@ -47,7 +48,34 @@ VLOG_DEFINE_THIS_MODULE(route_table);

COVERAGE_DEFINE(route_table_dump);

struct route_data_nexthop {
struct ovs_list nexthop_node;

struct in6_addr addr;
char ifname[IFNAMSIZ]; /* Interface name. */
};

struct route_data {
/* Routes can have multiple next hops per destination.
*
* Each next hop has its own set of attributes such as address family,
* interface and IP address.
*
* When retrieving information about a route from the kernel, in the case
* of multiple next hops, information is provided as nested attributes.
*
* A linked list with struct route_data_nexthop entries is used to store
* this information as we parse each attribute.
*
* For the common case of one next hop, the nexthops list will contain a
* single entry pointing to the struct route_data _primary_next_hop
* element.
*
* Any dynamically allocated list elements can be freed with a call to the
* route_data_destroy function. */
struct ovs_list nexthops;
struct route_data_nexthop _primary_next_hop;

/* Copied from struct rtmsg. */
unsigned char rtm_dst_len;
unsigned char rtm_protocol;
Expand All @@ -56,8 +84,6 @@ struct route_data {
/* Extracted from Netlink attributes. */
struct in6_addr rta_dst; /* 0 if missing. */
struct in6_addr rta_prefsrc; /* 0 if missing. */
struct in6_addr rta_gw;
char ifname[IFNAMSIZ]; /* Interface name. */
uint32_t mark;
uint32_t rta_table_id; /* 0 if missing. */
uint32_t rta_priority; /* 0 if missing. */
Expand Down Expand Up @@ -89,12 +115,24 @@ static bool route_table_valid = false;
static void route_table_reset(void);
static void route_table_handle_msg(const struct route_table_msg *);
static int route_table_parse(struct ofpbuf *, void *change);
static void route_table_change(const struct route_table_msg *, void *);
static void route_table_change(struct route_table_msg *, void *aux);
static void route_map_clear(void);

static void name_table_init(void);
static void name_table_change(const struct rtnetlink_change *, void *);

static void
route_data_destroy(struct route_data *rd)
{
struct route_data_nexthop *rdnh;

LIST_FOR_EACH_POP (rdnh, nexthop_node, &rd->nexthops) {
if (rdnh && rdnh != &rd->_primary_next_hop) {
free(rdnh);
}
}
}

uint64_t
route_table_get_change_seq(void)
{
Expand Down Expand Up @@ -190,6 +228,7 @@ route_table_dump_one_table(unsigned char id)
filtered = false;
}
route_table_handle_msg(&msg);
route_data_destroy(&msg.rd);
}
}
ofpbuf_uninit(&buf);
Expand Down Expand Up @@ -222,13 +261,12 @@ route_table_reset(void)
}
}

/* Return RTNLGRP_IPV4_ROUTE or RTNLGRP_IPV6_ROUTE on success, 0 on parse
* error. */
static int
route_table_parse__(struct ofpbuf *buf, size_t ofs,
const struct nlmsghdr *nlmsg,
const struct rtmsg *rtm, struct route_table_msg *change)
{
struct route_data_nexthop *rdnh = NULL;
bool parsed, ipv4 = false;

static const struct nl_policy policy[] = {
Expand Down Expand Up @@ -269,6 +307,12 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs,
int rta_oif; /* Output interface index. */

memset(change, 0, sizeof *change);

/* ovs_list_init / ovs_list_insert does not allocate any memory */
ovs_list_init(&change->rd.nexthops);
rdnh = &change->rd._primary_next_hop;
ovs_list_insert(&change->rd.nexthops, &rdnh->nexthop_node);

change->relevant = true;

if (rtm->rtm_scope == RT_SCOPE_NOWHERE) {
Expand All @@ -292,15 +336,15 @@ 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, change->rd.ifname)) {
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 {
return 0;
goto error_out;
}
}
}
Expand Down Expand Up @@ -330,9 +374,9 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs,
if (ipv4) {
ovs_be32 gw;
gw = nl_attr_get_be32(attrs[RTA_GATEWAY]);
in6_addr_set_mapped_ipv4(&change->rd.rta_gw, gw);
in6_addr_set_mapped_ipv4(&rdnh->addr, gw);
} else {
change->rd.rta_gw = nl_attr_get_in6_addr(attrs[RTA_GATEWAY]);
rdnh->addr = nl_attr_get_in6_addr(attrs[RTA_GATEWAY]);
}
}
if (attrs[RTA_MARK]) {
Expand All @@ -343,13 +387,27 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs,
}
} else {
VLOG_DBG_RL(&rl, "received unparseable rtnetlink route message");
return 0;
goto error_out;
}

/* Success. */
return ipv4 ? RTNLGRP_IPV4_ROUTE : RTNLGRP_IPV6_ROUTE;

error_out:
route_data_destroy(&change->rd);
return 0;
}

/* Parse Netlink message in buf, which is expected to contain UAPI rtmsg
* header and associated route attributes.
*
* Return RTNLGRP_IPV4_ROUTE or RTNLGRP_IPV6_ROUTE on success, 0 on parse
* error.
*
* On success, memory may have be allocated, and it is the callers
* responsibility to free it with a call to route_data_destroy.
*
* In case of error, any allocated memory will be freed before return. */
static int
route_table_parse(struct ofpbuf *buf, void *change)
{
Expand All @@ -373,24 +431,34 @@ route_table_standard_table(uint32_t table_id)
}

static void
route_table_change(const struct route_table_msg *change OVS_UNUSED,
route_table_change(struct route_table_msg *change,
void *aux OVS_UNUSED)
{
if (!change
|| (change->relevant
&& route_table_standard_table(change->rd.rta_table_id))) {
route_table_valid = false;
}
if (change) {
route_data_destroy(&change->rd);
}
}

static void
route_table_handle_msg(const struct route_table_msg *change)
{
if (change->relevant && change->nlmsg_type == RTM_NEWROUTE) {
if (change->relevant && change->nlmsg_type == RTM_NEWROUTE
&& ovs_list_is_singleton(&change->rd.nexthops)) {
const struct route_data *rd = &change->rd;
const struct route_data_nexthop *rdnh;

/* The ovs-router module does currently not implement lookup nor
* storage for routes with multiple next hops. */
rdnh = CONTAINER_OF(ovs_list_front(&change->rd.nexthops),
const struct route_data_nexthop, nexthop_node);

ovs_router_insert(rd->mark, &rd->rta_dst, rd->rtm_dst_len,
rd->local, rd->ifname, &rd->rta_gw,
rd->local, rdnh->ifname, &rdnh->addr,
&rd->rta_prefsrc);
}
}
Expand Down

0 comments on commit f62cbb8

Please sign in to comment.