Skip to content

Commit

Permalink
northd: Allow announcing individual host routes.
Browse files Browse the repository at this point in the history
sometimes we want to use individual host routes instead of the connected
routes of LRPs.
This allows the network fabric to know which adresses are actually in
use and e.g. drop traffic to adresses that are not used anyway.

Signed-off-by: Felix Huettner <[email protected]>
Signed-off-by: 0-day Robot <[email protected]>
  • Loading branch information
felixhuettner authored and ovsrobot committed Dec 18, 2024
1 parent 88752b2 commit dfe3acb
Show file tree
Hide file tree
Showing 8 changed files with 402 additions and 45 deletions.
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Post v24.09.0
Routes entered into the "Route" table in the southbound database will be
learned by the respective LR. They are included in the route table with
a lower priority than static routes.
- Add the option "dynamic-routing-connected-as-host-routes" to LRPs. If set
to true then connected routes are announced as individual host routes.

OVN v24.09.0 - 13 Sep 2024
--------------------------
Expand Down
270 changes: 247 additions & 23 deletions northd/en-advertised-route-sync.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
*/

#include <config.h>
#include <stdbool.h>

#include "openvswitch/vlog.h"
#include "smap.h"
#include "stopwatch.h"
#include "northd.h"

#include "en-advertised-route-sync.h"
#include "en-lr-stateful.h"
#include "lib/stopwatch-names.h"
#include "openvswitch/hmap.h"
#include "ovn-util.h"
Expand All @@ -30,34 +32,129 @@ static void
advertised_route_table_sync(
struct ovsdb_idl_txn *ovnsb_txn,
const struct sbrec_advertised_route_table *sbrec_advertised_route_table,
const struct hmap *parsed_routes);
const struct lr_stateful_table *lr_stateful_table,
const struct hmap *parsed_routes,
struct advertised_route_sync_tracked_data *trk_data);

bool
advertised_route_sync_lr_stateful_change_handler(struct engine_node *node,
void *data_)
{
/* We only actually use lr_stateful data if we expose individual host
* routes. In this case we for now just recompute.
* */
struct ed_type_lr_stateful *lr_stateful_data =
engine_get_input_data("lr_stateful", node);
struct advertised_route_sync_data *data = data_;

struct hmapx_node *hmapx_node;
const struct lr_stateful_record *lr_stateful_rec;
HMAPX_FOR_EACH (hmapx_node, &lr_stateful_data->trk_data.crupdated) {
lr_stateful_rec = hmapx_node->data;
if (uuidset_contains(&data->trk_data.nb_lr_stateful,
&lr_stateful_rec->nbr_uuid)) {
return false;
}
}

return true;
}

bool
advertised_route_sync_northd_change_handler(struct engine_node *node,
void *data_)
{
struct advertised_route_sync_data *data = data_;
struct northd_data *northd_data = engine_get_input_data("northd", node);
if (!northd_has_tracked_data(&northd_data->trk_data)) {
return false;
}

/* This node uses the below data from the en_northd engine node.
* See (lr_stateful_get_input_data())
* 1. Indirectly northd_data->ls_ports if we announce host routes
* This is what we check below
*/

struct hmapx_node *hmapx_node;
const struct ovn_port *op;
HMAPX_FOR_EACH (hmapx_node, &northd_data->trk_data.trk_lsps.created) {
op = hmapx_node->data;
if (uuidset_contains(&data->trk_data.nb_ls,
&op->od->nbs->header_.uuid)) {
return false;
}
}
HMAPX_FOR_EACH (hmapx_node, &northd_data->trk_data.trk_lsps.updated) {
op = hmapx_node->data;
if (uuidset_contains(&data->trk_data.nb_ls,
&op->od->nbs->header_.uuid)) {
return false;
}
}
HMAPX_FOR_EACH (hmapx_node, &northd_data->trk_data.trk_lsps.deleted) {
op = hmapx_node->data;
if (uuidset_contains(&data->trk_data.nb_ls,
&op->od->nbs->header_.uuid)) {
return false;
}
}

return true;
}

static void
routes_sync_init(struct advertised_route_sync_data *data)
{
uuidset_init(&data->trk_data.nb_lr_stateful);
uuidset_init(&data->trk_data.nb_ls);
}

static void
routes_sync_destroy(struct advertised_route_sync_data *data)
{
uuidset_destroy(&data->trk_data.nb_lr_stateful);
uuidset_destroy(&data->trk_data.nb_ls);
}


void
*en_advertised_route_sync_init(struct engine_node *node OVS_UNUSED,
struct engine_arg *arg OVS_UNUSED)
{
return NULL;
struct advertised_route_sync_data *data = xzalloc(sizeof *data);
routes_sync_init(data);
return data;
}

void
en_advertised_route_sync_cleanup(void *data OVS_UNUSED)
{
routes_sync_destroy(data);
}

void
en_advertised_route_sync_run(struct engine_node *node, void *data OVS_UNUSED)
{
routes_sync_destroy(data);
routes_sync_init(data);

struct advertised_route_sync_data *routes_sync_data = data;
struct routes_data *routes_data
= engine_get_input_data("routes", node);
const struct engine_context *eng_ctx = engine_get_context();
const struct sbrec_advertised_route_table *sbrec_advertised_route_table =
EN_OVSDB_GET(engine_get_input("SB_advertised_route", node));
struct ed_type_lr_stateful *lr_stateful_data =
engine_get_input_data("lr_stateful", node);

stopwatch_start(ADVERTISED_ROUTE_SYNC_RUN_STOPWATCH_NAME, time_msec());

advertised_route_table_sync(eng_ctx->ovnsb_idl_txn,
sbrec_advertised_route_table,
&routes_data->parsed_routes);
&lr_stateful_data->table,
&routes_data->parsed_routes,
&routes_sync_data->trk_data);

stopwatch_stop(ADVERTISED_ROUTE_SYNC_RUN_STOPWATCH_NAME, time_msec());
engine_set_node_state(node, EN_UPDATED);
Expand All @@ -71,20 +168,25 @@ struct ar_entry {

const struct sbrec_port_binding *logical_port;
char *ip_prefix;
const struct sbrec_port_binding *tracked_port;
bool stale;
};

static struct ar_entry *
ar_alloc_entry(struct hmap *routes,
const struct sbrec_datapath_binding *sb_db,
const struct sbrec_port_binding *logical_port,
const char *ip_prefix)
const char *ip_prefix,
const struct sbrec_port_binding *tracked_port)
{
struct ar_entry *route_e = xzalloc(sizeof *route_e);

route_e->sb_db = sb_db;
route_e->logical_port = logical_port;
route_e->ip_prefix = xstrdup(ip_prefix);
if (tracked_port) {
route_e->tracked_port = tracked_port;
}
route_e->stale = false;
uint32_t hash = uuid_hash(&sb_db->header_.uuid);
hash = hash_string(logical_port->logical_port, hash);
Expand All @@ -98,7 +200,8 @@ static struct ar_entry *
ar_lookup_or_add(struct hmap *route_map,
const struct sbrec_datapath_binding *sb_db,
const struct sbrec_port_binding *logical_port,
const char *ip_prefix)
const char *ip_prefix,
const struct sbrec_port_binding *tracked_port)
{
struct ar_entry *route_e;
uint32_t hash;
Expand All @@ -121,11 +224,50 @@ ar_lookup_or_add(struct hmap *route_map,
continue;
}

if (!tracked_port != !route_e->tracked_port) {
continue;
}

if (tracked_port && !uuid_equals(
&tracked_port->header_.uuid,
&route_e->tracked_port->header_.uuid)) {
continue;
}

return route_e;
}

route_e = ar_alloc_entry(route_map, sb_db,
logical_port, ip_prefix);
logical_port, ip_prefix, tracked_port);
return route_e;
}

static struct ar_entry *
ar_sync_to_sb(struct ovsdb_idl_txn *ovnsb_txn, struct hmap *route_map,
const struct sbrec_datapath_binding *sb_db,
const struct sbrec_port_binding *logical_port,
const char *ip_prefix,
const struct sbrec_port_binding *tracked_port)
{
struct ar_entry *route_e = ar_lookup_or_add(route_map,
sb_db,
logical_port,
ip_prefix,
tracked_port);
route_e->stale = false;

if (!route_e->sb_route) {
const struct sbrec_advertised_route *sr =
sbrec_advertised_route_insert(ovnsb_txn);
sbrec_advertised_route_set_datapath(sr, route_e->sb_db);
sbrec_advertised_route_set_logical_port(sr, route_e->logical_port);
sbrec_advertised_route_set_ip_prefix(sr, route_e->ip_prefix);
if (route_e->tracked_port) {
sbrec_advertised_route_set_tracked_port(sr, route_e->tracked_port);
}
route_e->sb_route = sr;
}

return route_e;
}

Expand All @@ -143,11 +285,91 @@ get_nbrp_or_nbr_option(const struct ovn_port *op, const char *key)
smap_get_bool(&op->od->nbr->options, key, false));
}

static void
publish_lport_addresses(struct ovsdb_idl_txn *ovnsb_txn,
struct hmap *route_map,
const struct sbrec_datapath_binding *sb_db,
const struct ovn_port *logical_port,
struct lport_addresses *addresses,
const struct ovn_port *tracking_port)
{
for (int i = 0; i < addresses->n_ipv4_addrs; i++) {
const struct ipv4_netaddr *addr = &addresses->ipv4_addrs[i];
char *addr_s = xasprintf("%s/32", addr->addr_s);
ar_sync_to_sb(ovnsb_txn, route_map,
sb_db,
logical_port->sb,
addr_s,
tracking_port->sb);
free(addr_s);
}
for (int i = 0; i < addresses->n_ipv6_addrs; i++) {
if (in6_is_lla(&addresses->ipv6_addrs[i].network)) {
continue;
}
const struct ipv6_netaddr *addr = &addresses->ipv6_addrs[i];
char *addr_s = xasprintf("%s/128", addr->addr_s);
ar_sync_to_sb(ovnsb_txn, route_map,
sb_db,
logical_port->sb,
addr_s,
tracking_port->sb);
free(addr_s);
}
}


static void
publish_host_routes(struct ovsdb_idl_txn *ovnsb_txn,
struct hmap *route_map,
const struct lr_stateful_table *lr_stateful_table,
const struct parsed_route *route,
struct advertised_route_sync_tracked_data *trk_data)
{
struct ovn_port *port;
struct ovn_datapath *lsp_od = route->out_port->peer->od;
uuidset_insert(&trk_data->nb_ls, &lsp_od->nbs->header_.uuid);
HMAP_FOR_EACH (port, dp_node, &lsp_od->ports) {
if (port->peer) {
/* This is a LSP connected to an LRP */
struct lport_addresses *addresses = &port->peer->lrp_networks;
publish_lport_addresses(ovnsb_txn, route_map, route->od->sb,
route->out_port,
addresses, port->peer);

const struct lr_stateful_record *lr_stateful_rec;
lr_stateful_rec = lr_stateful_table_find_by_index(
lr_stateful_table, port->peer->od->index);
uuidset_insert(&trk_data->nb_lr_stateful,
&lr_stateful_rec->nbr_uuid);
struct ovn_port_routable_addresses addrs = get_op_addresses(
port->peer, lr_stateful_rec, false);
for (int i = 0; i < addrs.n_addrs; i++) {
publish_lport_addresses(ovnsb_txn, route_map, route->od->sb,
route->out_port,
&addrs.laddrs[i],
port->peer);
}
destroy_routable_addresses(&addrs);
} else {
/* This is just a plain LSP */
for (int i = 0; i < port->n_lsp_addrs; i++) {
publish_lport_addresses(ovnsb_txn, route_map, route->od->sb,
route->out_port,
&port->lsp_addrs[i],
port);
}
}
}
}

static void
advertised_route_table_sync(
struct ovsdb_idl_txn *ovnsb_txn,
const struct sbrec_advertised_route_table *sbrec_advertised_route_table,
const struct hmap *parsed_routes)
const struct lr_stateful_table *lr_stateful_table,
const struct hmap *parsed_routes,
struct advertised_route_sync_tracked_data *trk_data)
{
if (!ovnsb_txn) {
return;
Expand All @@ -164,7 +386,8 @@ advertised_route_table_sync(
route_e = ar_alloc_entry(&sync_routes,
sb_route->datapath,
sb_route->logical_port,
sb_route->ip_prefix);
sb_route->ip_prefix,
sb_route->tracked_port);
route_e->stale = true;
route_e->sb_route = sb_route;
}
Expand All @@ -180,10 +403,18 @@ advertised_route_table_sync(
false)) {
continue;
}
if (route->source == ROUTE_SOURCE_CONNECTED &&
!get_nbrp_or_nbr_option(route->out_port,
if (route->source == ROUTE_SOURCE_CONNECTED) {
if (!get_nbrp_or_nbr_option(route->out_port,
"dynamic-routing-connected")) {
continue;
continue;
}
if (smap_get_bool(&route->out_port->nbrp->options,
"dynamic-routing-connected-as-host-routes",
false)) {
publish_host_routes(ovnsb_txn, &sync_routes,
lr_stateful_table, route, trk_data);
continue;
}
}
if (route->source == ROUTE_SOURCE_STATIC &&
!get_nbrp_or_nbr_option(route->out_port,
Expand All @@ -193,18 +424,11 @@ advertised_route_table_sync(

char *ip_prefix = normalize_v46_prefix(&route->prefix,
route->plen);
route_e = ar_lookup_or_add(&sync_routes, route->od->sb,
route->out_port->sb, ip_prefix);
route_e->stale = false;

if (!route_e->sb_route) {
const struct sbrec_advertised_route *sr =
sbrec_advertised_route_insert(ovnsb_txn);
sbrec_advertised_route_set_datapath(sr, route_e->sb_db);
sbrec_advertised_route_set_logical_port(sr, route_e->logical_port);
sbrec_advertised_route_set_ip_prefix(sr, route_e->ip_prefix);
route_e->sb_route = sr;
}
ar_sync_to_sb(ovnsb_txn, &sync_routes,
route->od->sb,
route->out_port->sb,
ip_prefix,
NULL);

free(ip_prefix);
}
Expand Down
Loading

0 comments on commit dfe3acb

Please sign in to comment.