This change builds on top of the new "dynamic routing" OVN feature
that allows advertising routes to the fabric network. New accepted
values are added to "dynamic-routing-redistribute" option.
* nat - When included in the option, ovn-controller will advertise
routes for external NAT IPs of the LR, as well as those
of the neighboring routers (directly connected or through
the shared LS).
* lb - When included in the option, ovn-controller will advertise
routes for LB VIPs of the LR, as well as those
of the neighboring routers (directly connected or through
the shared LS).
Co-authored-by: Frode Nordahl <[email protected]>
Co-authored-by: Dumitru Ceara <[email protected]>
Signed-off-by: Frode Nordahl <[email protected]>
Signed-off-by: Dumitru Ceara <[email protected]>
Signed-off-by: Martin Kalcok <[email protected]>
---
TODO.rst | 6 +
lib/stopwatch-names.h | 1 +
northd/en-advertised-route-sync.c | 202 ++++++++--
northd/en-advertised-route-sync.h | 4 +
northd/en-learned-route-sync.c | 3 +-
northd/inc-proc-northd.c | 6 +
northd/northd.c | 246 +++++++++++-
northd/northd.h | 32 +-
ovn-nb.xml | 36 ++
tests/ovn-northd.at | 601 ++++++++++++++++++++++++++++
tests/system-ovn.at | 628 ++++++++++++++++++++++++++++++
11 files changed, 1716 insertions(+), 49 deletions(-)
diff --git a/TODO.rst b/TODO.rst
index d75db3152..c50b2b980 100644
--- a/TODO.rst
+++ b/TODO.rst
@@ -156,3 +156,9 @@ OVN To-do List
monitoring conditions to update before we actually try to learn routes.
Otherwise we could try to add duplicated Learned_Routes and the ovnsb
commit would fail.
+ * Consider splitting parsed_route structure. When creating parsed routes
+ with tracked_port explicitly set, other members of this structure are
+ usually unused/default. A new structure dedicated to routes with
+ explicitly defined tracked_port would be more efficient.
+ More details in
+ https://mail.openvswitch.org/pipermail/ovs-dev/2025-February/420985.html
diff --git a/lib/stopwatch-names.h b/lib/stopwatch-names.h
index c12dd28d0..13aa5e7bf 100644
--- a/lib/stopwatch-names.h
+++ b/lib/stopwatch-names.h
@@ -36,5 +36,6 @@
#define LS_STATEFUL_RUN_STOPWATCH_NAME "ls_stateful"
#define ADVERTISED_ROUTE_SYNC_RUN_STOPWATCH_NAME "advertised_route_sync"
#define LEARNED_ROUTE_SYNC_RUN_STOPWATCH_NAME "learned_route_sync"
+#define DYNAMIC_ROUTES_RUN_STOPWATCH_NAME "dynamic_routes"
#endif
diff --git a/northd/en-advertised-route-sync.c
b/northd/en-advertised-route-sync.c
index cfea7c39b..279020a25 100644
--- a/northd/en-advertised-route-sync.c
+++ b/northd/en-advertised-route-sync.c
@@ -30,7 +30,8 @@ advertised_route_table_sync(
struct ovsdb_idl_txn *ovnsb_txn,
const struct sbrec_advertised_route_table *sbrec_advertised_route_table,
const struct lr_stateful_table *lr_stateful_table,
- const struct hmap *parsed_routes,
+ const struct hmap *routes,
+ const struct hmap *dynamic_routes,
struct advertised_route_sync_data *data);
bool
@@ -141,6 +142,8 @@ en_advertised_route_sync_run(struct engine_node *node, void
*data OVS_UNUSED)
struct advertised_route_sync_data *routes_sync_data = data;
struct routes_data *routes_data
= engine_get_input_data("routes", node);
+ struct dynamic_routes_data *dynamic_routes_data
+ = engine_get_input_data("dynamic_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));
@@ -153,12 +156,84 @@ en_advertised_route_sync_run(struct engine_node *node,
void *data OVS_UNUSED)
sbrec_advertised_route_table,
&lr_stateful_data->table,
&routes_data->parsed_routes,
+ &dynamic_routes_data->parsed_routes,
routes_sync_data);
stopwatch_stop(ADVERTISED_ROUTE_SYNC_RUN_STOPWATCH_NAME, time_msec());
engine_set_node_state(node, EN_UPDATED);
}
+void
+*en_dynamic_routes_init(struct engine_node *node OVS_UNUSED,
+ struct engine_arg *arg OVS_UNUSED)
+{
+ struct dynamic_routes_data *data = xmalloc(sizeof *data);
+ *data = (struct dynamic_routes_data) {
+ .parsed_routes = HMAP_INITIALIZER(&data->parsed_routes),
+ };
+
+ return data;
+}
+
+static void
+en_dynamic_routes_clean(struct dynamic_routes_data *data)
+{
+ struct parsed_route *r;
+ HMAP_FOR_EACH_POP (r, key_node, &data->parsed_routes) {
+ parsed_route_free(r);
+ }
+}
+void
+en_dynamic_routes_cleanup(void *data_)
+{
+ struct dynamic_routes_data *data = data_;
+
+ en_dynamic_routes_clean(data);
+ hmap_destroy(&data->parsed_routes);
+}
+
+void
+en_dynamic_routes_run(struct engine_node *node, void *data)
+{
+ struct dynamic_routes_data *dynamic_routes_data = data;
+ struct northd_data *northd_data = engine_get_input_data("northd", node);
+ struct ed_type_lr_stateful *lr_stateful_data =
+ engine_get_input_data("lr_stateful", node);
+
+ en_dynamic_routes_clean(data);
+ const struct lr_stateful_record *lr_stateful_rec;
+ HMAP_FOR_EACH (lr_stateful_rec, key_node,
+ &lr_stateful_data->table.entries) {
+ const struct ovn_datapath *od =
+ ovn_datapaths_find_by_index(&northd_data->lr_datapaths,
+ lr_stateful_rec->lr_index);
+ if (!od->dynamic_routing) {
+ continue;
+ }
+ build_nat_parsed_routes(od, lr_stateful_rec->lrnat_rec,
+ &dynamic_routes_data->parsed_routes);
+ build_nat_connected_parsed_routes(od, &lr_stateful_data->table,
+ &dynamic_routes_data->parsed_routes);
+
+ build_lb_parsed_routes(od, lr_stateful_rec->lb_ips,
+ &dynamic_routes_data->parsed_routes);
+ build_lb_connected_parsed_routes(od, &lr_stateful_data->table,
+ &dynamic_routes_data->parsed_routes);
+ }
+ engine_set_node_state(node, EN_UPDATED);
+}
+
+bool
+dynamic_routes_lr_stateful_handler(struct engine_node *node OVS_UNUSED,
+ void *data OVS_UNUSED)
+{
+ /* XXX: Incremental processing of dynamic routes for stateful
+ * configuration changes is not yet supported. Return false and
+ * trigger a recomputation.*/
+ return false;
+}
+
+
struct ar_entry {
struct hmap_node hmap_node;
@@ -333,12 +408,84 @@ publish_host_routes(struct hmap *sync_routes,
}
}
+static void
+advertised_route_table_sync_route_add(
+ const struct lr_stateful_table *lr_stateful_table,
+ struct advertised_route_sync_data *data,
+ struct uuidset *host_route_lrps,
+ struct hmap *sync_routes,
+ const struct parsed_route *route)
+{
+ if (route->is_discard_route) {
+ return;
+ }
+ if (prefix_is_link_local(&route->prefix, route->plen)) {
+ return;
+ }
+ if (!route->od->dynamic_routing) {
+ return;
+ }
+
+ enum dynamic_routing_redistribute_mode drr =
+ route->out_port->dynamic_routing_redistribute;
+ if (route->source == ROUTE_SOURCE_CONNECTED) {
+ if (!drr_mode_CONNECTED_is_set(drr)) {
+ return;
+ }
+ /* If we advertise host routes, we only need to do so once per
+ * LRP. */
+ const struct uuid *lrp_uuid = &route->out_port->nbrp->header_.uuid;
+ if (drr_mode_CONNECTED_AS_HOST_is_set(drr) &&
+ !uuidset_contains(host_route_lrps, lrp_uuid)) {
+ uuidset_insert(host_route_lrps, lrp_uuid);
+ publish_host_routes(sync_routes, lr_stateful_table, route, data);
+ return;
+ }
+ }
+ if (route->source == ROUTE_SOURCE_STATIC && !drr_mode_STATIC_is_set(drr)) {
+ return;
+ }
+ if (route->source == ROUTE_SOURCE_NAT) {
+ if (!drr_mode_NAT_is_set(drr)) {
+ return;
+ }
+ /* If NAT route tracks port on a different DP than the one that
+ * advertises the route, we need to watch for changes on that DP as
+ * well. */
+ if (route->tracked_port && route->tracked_port->od != route->od) {
+ uuidset_insert(&data->nb_lr,
+ &route->tracked_port->od->nbr->header_.uuid);
+ }
+ }
+ if (route->source == ROUTE_SOURCE_LB) {
+ if (!drr_mode_LB_is_set(drr)) {
+ return;
+ }
+ /* If LB route tracks port on a different DP than the one that
+ * advertises the route, we need to watch for changes on that DP as
+ * well. */
+ if (route->tracked_port && route->tracked_port->od != route->od) {
+ uuidset_insert(&data->nb_lr,
+ &route->tracked_port->od->nbr->header_.uuid);
+ }
+ }
+
+ char *ip_prefix = normalize_v46_prefix(&route->prefix, route->plen);
+ const struct sbrec_port_binding *tracked_port = NULL;
+ if (route->tracked_port) {
+ tracked_port = route->tracked_port->sb;
+ }
+ ar_add_entry(sync_routes, route->od->sb, route->out_port->sb,
+ ip_prefix, tracked_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 lr_stateful_table *lr_stateful_table,
- const struct hmap *parsed_routes,
+ const struct hmap *routes,
+ const struct hmap *dynamic_routes,
struct advertised_route_sync_data *data)
{
struct hmap sync_routes = HMAP_INITIALIZER(&sync_routes);
@@ -346,47 +493,24 @@ advertised_route_table_sync(
const struct parsed_route *route;
struct ar_entry *route_e;
- const struct sbrec_advertised_route *sb_route;
- HMAP_FOR_EACH (route, key_node, parsed_routes) {
- if (route->is_discard_route) {
- continue;
- }
- if (prefix_is_link_local(&route->prefix, route->plen)) {
- continue;
- }
- if (!route->od->dynamic_routing) {
- continue;
- }
- enum dynamic_routing_redistribute_mode drr =
- route->out_port->dynamic_routing_redistribute;
- if (route->source == ROUTE_SOURCE_CONNECTED) {
- if (!drr_mode_CONNECTED_is_set(drr)) {
- continue;
- }
- /* If we advertise host routes, we only need to do so once per
- * LRP. */
- const struct uuid *lrp_uuid =
- &route->out_port->nbrp->header_.uuid;
- if (drr_mode_CONNECTED_AS_HOST_is_set(drr) &&
- !uuidset_contains(&host_route_lrps, lrp_uuid)) {
- uuidset_insert(&host_route_lrps, lrp_uuid);
- publish_host_routes(&sync_routes, lr_stateful_table,
- route, data);
- continue;
- }
- }
- if (route->source == ROUTE_SOURCE_STATIC &&
- !drr_mode_STATIC_is_set(drr)) {
- continue;
- }
-
- char *ip_prefix = normalize_v46_prefix(&route->prefix, route->plen);
- route_e = ar_add_entry(&sync_routes, route->od->sb,
- route->out_port->sb, ip_prefix, NULL);
+ /* First build the set of non-dynamic routes that need sync-ing. */
+ HMAP_FOR_EACH (route, key_node, routes) {
+ advertised_route_table_sync_route_add(lr_stateful_table,
+ data, &host_route_lrps,
+ &sync_routes,
+ route);
+ }
+ /* Then add the set of dynamic routes that need sync-ing. */
+ HMAP_FOR_EACH (route, key_node, dynamic_routes) {
+ advertised_route_table_sync_route_add(lr_stateful_table,
+ data, &host_route_lrps,
+ &sync_routes,
+ route);
}
uuidset_destroy(&host_route_lrps);
+ const struct sbrec_advertised_route *sb_route;
SBREC_ADVERTISED_ROUTE_TABLE_FOR_EACH_SAFE (sb_route,
sbrec_advertised_route_table) {
route_e = ar_find(&sync_routes, sb_route->datapath,
diff --git a/northd/en-advertised-route-sync.h
b/northd/en-advertised-route-sync.h
index 1f24fd329..e18b75643 100644
--- a/northd/en-advertised-route-sync.h
+++ b/northd/en-advertised-route-sync.h
@@ -36,4 +36,8 @@ void *en_advertised_route_sync_init(struct engine_node *,
struct engine_arg *);
void en_advertised_route_sync_cleanup(void *data);
void en_advertised_route_sync_run(struct engine_node *, void *data);
+bool dynamic_routes_lr_stateful_handler(struct engine_node *, void *data);
+void *en_dynamic_routes_init(struct engine_node *, struct engine_arg *);
+void en_dynamic_routes_cleanup(void *data);
+void en_dynamic_routes_run(struct engine_node *, void *data);
#endif /* EN_ADVERTISED_ROUTE_SYNC_H */
diff --git a/northd/en-learned-route-sync.c b/northd/en-learned-route-sync.c
index 406f1551f..4e87b3265 100644
--- a/northd/en-learned-route-sync.c
+++ b/northd/en-learned-route-sync.c
@@ -181,7 +181,8 @@ parse_route_from_sbrec_route(struct hmap *parsed_routes_out,
parsed_route_add(od, nexthop, &prefix, plen, false, lrp_addr_s,
out_port, 0, false, false, NULL,
- ROUTE_SOURCE_LEARNED, &route->header_, parsed_routes_out);
+ ROUTE_SOURCE_LEARNED, &route->header_, NULL,
+ parsed_routes_out);
}
static void
diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
index 438daf1c6..1bdc10e6a 100644
--- a/northd/inc-proc-northd.c
+++ b/northd/inc-proc-northd.c
@@ -175,6 +175,7 @@ static ENGINE_NODE(multicast_igmp, "multicast_igmp");
static ENGINE_NODE(acl_id, "acl_id");
static ENGINE_NODE(advertised_route_sync, "advertised_route_sync");
static ENGINE_NODE(learned_route_sync, "learned_route_sync");
+static ENGINE_NODE(dynamic_routes, "dynamic_routes");
void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
struct ovsdb_idl_loop *sb)
@@ -289,7 +290,12 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
engine_add_input(&en_ecmp_nexthop, &en_sb_mac_binding,
ecmp_nexthop_mac_binding_handler);
+ engine_add_input(&en_dynamic_routes, &en_lr_stateful,
+ dynamic_routes_lr_stateful_handler);
+ engine_add_input(&en_dynamic_routes, &en_northd, engine_noop_handler);
+
engine_add_input(&en_advertised_route_sync, &en_routes, NULL);
+ engine_add_input(&en_advertised_route_sync, &en_dynamic_routes, NULL);
engine_add_input(&en_advertised_route_sync, &en_sb_advertised_route,
NULL);
engine_add_input(&en_advertised_route_sync, &en_lr_stateful,
diff --git a/northd/northd.c b/northd/northd.c
index 1c9433e96..e9866e7be 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -848,6 +848,14 @@ parse_dynamic_routing_redistribute(
out |= DRRM_STATIC;
continue;
}
+ if (!strcmp(token, "nat")) {
+ out |= DRRM_NAT;
+ continue;
+ }
+ if (!strcmp(token, "lb")) {
+ out |= DRRM_LB;
+ continue;
+ }
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
VLOG_WARN_RL(&rl,
"unkown dynamic-routing-redistribute option '%s' on %s",
@@ -11012,6 +11020,7 @@ parsed_route_init(const struct ovn_datapath *od,
bool ecmp_symmetric_reply,
const struct sset *ecmp_selection_fields,
enum route_source source,
+ const struct ovn_port *tracked_port,
const struct ovsdb_idl_row *source_hint)
{
@@ -11027,6 +11036,7 @@ parsed_route_init(const struct ovn_datapath *od,
new_pr->is_discard_route = is_discard_route;
new_pr->lrp_addr_s = nullable_xstrdup(lrp_addr_s);
new_pr->out_port = out_port;
+ new_pr->tracked_port = tracked_port;
new_pr->source = source;
if (ecmp_selection_fields) {
sset_clone(&new_pr->ecmp_selection_fields, ecmp_selection_fields);
@@ -11052,7 +11062,7 @@ parsed_route_clone(const struct parsed_route *pr)
pr->od, nexthop, pr->prefix, pr->plen, pr->is_discard_route,
pr->lrp_addr_s, pr->out_port, pr->route_table_id, pr->is_src_route,
pr->ecmp_symmetric_reply, &pr->ecmp_selection_fields, pr->source,
- pr->source_hint);
+ pr->tracked_port, pr->source_hint);
new_pr->hash = pr->hash;
return new_pr;
@@ -11095,13 +11105,14 @@ parsed_route_add(const struct ovn_datapath *od,
const struct sset *ecmp_selection_fields,
enum route_source source,
const struct ovsdb_idl_row *source_hint,
+ const struct ovn_port *tracked_port,
struct hmap *routes)
{
struct parsed_route *new_pr = parsed_route_init(
od, nexthop, *prefix, plen, is_discard_route, lrp_addr_s, out_port,
route_table_id, is_src_route, ecmp_symmetric_reply,
- ecmp_selection_fields, source, source_hint);
+ ecmp_selection_fields, source, tracked_port, source_hint);
new_pr->hash = route_hash(new_pr);
@@ -11238,7 +11249,7 @@ parsed_routes_add_static(const struct ovn_datapath *od,
parsed_route_add(od, nexthop, &prefix, plen, is_discard_route, lrp_addr_s,
out_port, route_table_id, is_src_route,
ecmp_symmetric_reply, &ecmp_selection_fields, source,
- &route->header_, routes);
+ &route->header_, NULL, routes);
sset_destroy(&ecmp_selection_fields);
}
@@ -11256,7 +11267,7 @@ parsed_routes_add_connected(const struct ovn_datapath
*od,
false, addr->addr_s, op,
0, false,
false, NULL, ROUTE_SOURCE_CONNECTED,
- &op->nbrp->header_, routes);
+ &op->nbrp->header_, NULL, routes);
}
for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
@@ -11268,7 +11279,7 @@ parsed_routes_add_connected(const struct ovn_datapath
*od,
false, addr->addr_s, op,
0, false,
false, NULL, ROUTE_SOURCE_CONNECTED,
- &op->nbrp->header_, routes);
+ &op->nbrp->header_, NULL, routes);
}
}
@@ -11306,6 +11317,229 @@ build_parsed_routes(const struct ovn_datapath *od,
const struct hmap *lr_ports,
}
}
+/* This function adds new parsed route for each entry in lr_nat record
+ * to "routes". Logical port of the route is set to "advertising_op" and
+ * tracked port is set to NAT's distributed gw port. If NAT doesn't have
+ * DGP (for example if it's set on gateway router), no tracked port will
+ * be set.*/
+static void
+build_nat_parsed_route_for_port(const struct ovn_port *advertising_op,
+ const struct lr_nat_record *lr_nat,
+ struct hmap *routes)
+{
+ const struct ovn_datapath *advertising_od = advertising_op->od;
+
+ for (size_t i = 0; i < lr_nat->n_nat_entries; i++) {
+ const struct ovn_nat *nat = &lr_nat->nat_entries[i];
+ int plen = nat_entry_is_v6(nat) ? 128 : 32;
+ struct in6_addr prefix;
+ ip46_parse(nat->nb->external_ip, &prefix);
+ parsed_route_add(advertising_od, NULL, &prefix, plen, false,
+ nat->nb->external_ip, advertising_op, 0, false,
+ false, NULL, ROUTE_SOURCE_NAT, &nat->nb->header_,
+ nat->l3dgw_port, routes);
+ }
+}
+
+/* Generate parsed routes for NAT external IPs in lr_nat, for each ovn port
+ * in "od" that has enabled redistribution of NAT adresses.*/
+void
+build_nat_parsed_routes(const struct ovn_datapath *od,
+ const struct lr_nat_record *lr_nat,
+ struct hmap *routes)
+{
+ const struct ovn_port *op;
+ HMAP_FOR_EACH (op, dp_node, &od->ports) {
+ if (!drr_mode_NAT_is_set(op->dynamic_routing_redistribute)) {
+ continue;
+ }
+
+ build_nat_parsed_route_for_port(op, lr_nat, routes);
+ }
+}
+
+/* Similar to build_nat_parsed_routes, this function generates parsed routes
+ * for nat records in neighboring routers. For each ovn port in "od" that has
+ * enabled redistribution of NAT adresses, look up their neighbors (either
+ * directly routers, or routers connected through common LS) and advertise
+ * thier external NAT IPs too.*/
+void
+build_nat_connected_parsed_routes(
+ const struct ovn_datapath *od,
+ const struct lr_stateful_table *lr_stateful_table,
+ struct hmap *routes)
+{
+ const struct ovn_port *op;
+ HMAP_FOR_EACH (op, dp_node, &od->ports) {
+ if (!drr_mode_NAT_is_set(op->dynamic_routing_redistribute)) {
+ continue;
+ }
+
+ if (!op->peer) {
+ continue;
+ }
+
+ struct ovn_datapath *peer_od = op->peer->od;
+ if (!peer_od->nbs && !peer_od->nbr) {
+ continue;
+ }
+
+ const struct ovn_port *peer_port = NULL;
+ /* This is a directly connected LR peer. */
+ if (peer_od->nbr) {
+ peer_port = op->peer;
+
+ const struct lr_stateful_record *peer_lr_stateful =
+ lr_stateful_table_find_by_index(lr_stateful_table,
+ peer_od->index);
+ if (!peer_lr_stateful) {
+ continue;
+ }
+
+ /* Advertise peer's NAT routes via the local port too. */
+ build_nat_parsed_route_for_port(op, peer_lr_stateful->lrnat_rec,
+ routes);
+ return;
+ }
+
+ /* This peer is LSP, we need to check all connected router ports
+ * for NAT.*/
+ for (size_t i = 0; i < peer_od->n_router_ports; i++) {
+ peer_port = peer_od->router_ports[i]->peer;
+ if (peer_port == op) {
+ /* Skip advertising router. */
+ continue;
+ }
+
+ const struct lr_stateful_record *peer_lr_stateful =
+ lr_stateful_table_find_by_index(lr_stateful_table,
+ peer_port->od->index);
+ if (!peer_lr_stateful) {
+ continue;
+ }
+
+ /* Advertise peer's NAT routes via the local port too. */
+ build_nat_parsed_route_for_port(op, peer_lr_stateful->lrnat_rec,
+ routes);
+ }
+ }
+}
+
+/* This function adds new parsed route for each IP in lb_ips to "routes".*/
+static void
+build_lb_parsed_route_for_port(const struct ovn_port *advertising_op,
+ const struct ovn_port *tracked_port,
+ const struct ovn_lb_ip_set *lb_ips,
+ struct hmap *routes)
+{
+ const struct ovn_datapath *advertising_od = advertising_op->od;
+
+ const char *ip_address;
+ SSET_FOR_EACH (ip_address, &lb_ips->ips_v4) {
+ struct in6_addr prefix;
+ ip46_parse(ip_address, &prefix);
+ parsed_route_add(advertising_od, NULL, &prefix, 32, false,
+ ip_address, advertising_op, 0, false, false,
+ NULL, ROUTE_SOURCE_LB, &advertising_op->nbrp->header_,
+ tracked_port, routes);
+ }
+ SSET_FOR_EACH (ip_address, &lb_ips->ips_v6) {
+ struct in6_addr prefix;
+ ip46_parse(ip_address, &prefix);
+ parsed_route_add(advertising_od, NULL, &prefix, 128, false,
+ ip_address, advertising_op, 0, false, false,
+ NULL, ROUTE_SOURCE_LB, &advertising_op->nbrp->header_,
+ tracked_port, routes);
+ }
+}
+
+/* Similar to build_lb_parsed_routes, this function generates parsed routes
+ * for LB VIPs of neighboring routers. For each ovn port in "od" that has
+ * enabled redistribution of LB VIPs, look up their neighbors (either
+ * directly routers, or routers connected through common LS) and advertise
+ * thier LB VIPs too.*/
+void
+build_lb_connected_parsed_routes(
+ const struct ovn_datapath *od,
+ const struct lr_stateful_table *lr_stateful_table,
+ struct hmap *routes)
+{
+ const struct ovn_port *op;
+ HMAP_FOR_EACH (op, dp_node, &od->ports) {
+ if (!drr_mode_LB_is_set(op->dynamic_routing_redistribute)) {
+ continue;
+ }
+
+ if (!op->peer) {
+ continue;
+ }
+
+ struct ovn_datapath *peer_od = op->peer->od;
+ if (!peer_od->nbs && !peer_od->nbr) {
+ continue;
+ }
+
+ const struct lr_stateful_record *lr_stateful_rec;
+ const struct ovn_port *peer_port = NULL;
+ /* This is directly connected LR peer. */
+ if (peer_od->nbr) {
+ lr_stateful_rec = lr_stateful_table_find_by_index(
+ lr_stateful_table, peer_od->index);
+ peer_port = op->peer;
+ build_lb_parsed_route_for_port(op, peer_port,
+ lr_stateful_rec->lb_ips, routes);
+ return;
+ }
+
+ /* This peer is LSP, we need to check all connected router ports for
+ * LBs.*/
+ for (size_t i = 0; i < peer_od->n_router_ports; i++) {
+ peer_port = peer_od->router_ports[i]->peer;
+ if (peer_port == op) {
+ /* no need to check for LBs on ovn_port that initiated this
+ * function.*/
+ continue;
+ }
+ lr_stateful_rec = lr_stateful_table_find_by_index(
+ lr_stateful_table, peer_port->od->index);
+
+ build_lb_parsed_route_for_port(op, peer_port,
+ lr_stateful_rec->lb_ips, routes);
+ }
+ }
+}
+
+void
+build_lb_parsed_routes(const struct ovn_datapath *od,
+ const struct ovn_lb_ip_set *lb_ips,
+ struct hmap *routes)
+{
+ const struct ovn_port *op;
+ HMAP_FOR_EACH (op, dp_node, &od->ports) {
+ if (!drr_mode_LB_is_set(op->dynamic_routing_redistribute)) {
+ continue;
+ }
+
+ /* Traffic processed by a load balancer is:
+ * - handled by the chassis where a gateway router is bound
+ * OR
+ * - always redirected to a distributed gateway router port
+ *
+ * Advertise the LB IPs via all 'op' if this is a gateway router or
+ * throuh all DGPs of this distributed router otherwise. */
+ struct ovn_port *op_ = NULL;
+ size_t n_tracked_ports = !od->is_gw_router ? od->n_l3dgw_ports : 1;
+ struct ovn_port **tracked_ports = !od->is_gw_router
+ ? od->l3dgw_ports
+ : &op_;
+
+ for (size_t i = 0; i < n_tracked_ports; i++) {
+ build_lb_parsed_route_for_port(op, tracked_ports[i], lb_ips,
+ routes);
+ }
+ }
+
+}
struct ecmp_route_list_node {
struct ovs_list list_node;
uint16_t id; /* starts from 1 */
@@ -11480,6 +11714,8 @@ route_source_to_offset(enum route_source source)
return ROUTE_PRIO_OFFSET_STATIC;
case ROUTE_SOURCE_LEARNED:
return ROUTE_PRIO_OFFSET_LEARNED;
+ case ROUTE_SOURCE_NAT:
+ case ROUTE_SOURCE_LB:
default:
OVS_NOT_REACHED();
}
diff --git a/northd/northd.h b/northd/northd.h
index b984e124d..a767fd834 100644
--- a/northd/northd.h
+++ b/northd/northd.h
@@ -186,11 +186,15 @@ struct route_policy {
};
struct routes_data {
- struct hmap parsed_routes;
+ struct hmap parsed_routes; /* Stores struct parsed_route. */
struct simap route_tables;
struct hmap bfd_active_connections;
};
+struct dynamic_routes_data {
+ struct hmap parsed_routes; /* Stores struct parsed_route. */
+};
+
struct route_policies_data {
struct hmap route_policies;
struct hmap bfd_active_connections;
@@ -308,10 +312,12 @@ struct mcast_port_info {
* (e.g., IGMP join/leave). */
};
-#define DRR_MODES \
- DRR_MODE(CONNECTED, 0) \
+#define DRR_MODES \
+ DRR_MODE(CONNECTED, 0) \
DRR_MODE(CONNECTED_AS_HOST, 1) \
- DRR_MODE(STATIC, 2)
+ DRR_MODE(STATIC, 2) \
+ DRR_MODE(NAT, 3) \
+ DRR_MODE(LB, 4)
enum dynamic_routing_redistribute_mode_bits {
#define DRR_MODE(PROTOCOL, BIT) DRRM_##PROTOCOL##_BIT = BIT,
@@ -746,6 +752,10 @@ enum route_source {
ROUTE_SOURCE_STATIC,
/* The route is dynamically learned by an ovn-controller. */
ROUTE_SOURCE_LEARNED,
+ /* The route is derived from a NAT's external IP. */
+ ROUTE_SOURCE_NAT,
+ /* The route is derived from a LB's VIP. */
+ ROUTE_SOURCE_LB,
};
struct parsed_route {
@@ -765,6 +775,7 @@ struct parsed_route {
const struct ovsdb_idl_row *source_hint;
char *lrp_addr_s;
const struct ovn_port *out_port;
+ const struct ovn_port *tracked_port; /* May be NULL. */
};
struct parsed_route *parsed_route_clone(const struct parsed_route *);
@@ -784,6 +795,7 @@ void parsed_route_add(const struct ovn_datapath *od,
const struct sset *ecmp_selection_fields,
enum route_source source,
const struct ovsdb_idl_row *source_hint,
+ const struct ovn_port *tracked_port,
struct hmap *routes);
bool
@@ -818,6 +830,18 @@ void route_policies_destroy(struct route_policies_data *);
void build_parsed_routes(const struct ovn_datapath *, const struct hmap *,
const struct hmap *, struct hmap *, struct simap *,
struct hmap *);
+void build_nat_parsed_routes(const struct ovn_datapath *,
+ const struct lr_nat_record *,
+ struct hmap *);
+void build_nat_connected_parsed_routes(const struct ovn_datapath *,
+ const struct lr_stateful_table *,
+ struct hmap *routes);
+void build_lb_parsed_routes(const struct ovn_datapath *,
+ const struct ovn_lb_ip_set *,
+ struct hmap *);
+void build_lb_connected_parsed_routes(const struct ovn_datapath *,
+ const struct lr_stateful_table *,
+ struct hmap *routes);
uint32_t get_route_table_id(struct simap *, const char *);
void routes_init(struct routes_data *);
void routes_destroy(struct routes_data *);
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 20d30dd58..bd3d2bf3e 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -3101,6 +3101,24 @@ or
<ref table="Advertised_Route" db="OVN_Southbound"/> table.
</p>
+ <p>
+ If <code>nb</code> is in the list then northd will create entries in
+ <ref table="Advertised_Route" db="OVN_Southbound"/> table for each
+ Load Balancer VIP on this router and it's neighboring routers.
+ Neighboring routers are those that are either directly connected,
+ via Logical Router Port, or those that are connected via shared
+ Logical Switch.
+ </p>
+
+ <p>
+ If <code>nat</code> is in the list then northd will create entries in
+ <ref table="Advertised_Route" db="OVN_Southbound"/> table for each
+ NAT's external IP on this router and it's neighboring routers.
+ Neighboring routers are those that are either directly connected,
+ via Logical Router Port, or those that are connected via shared
+ Logical Switch.
+ </p>
+
<p>
This value can be overwritten on a per LRP basis using
<ref column="options" key="dynamic-routing-redistribute"
@@ -3950,6 +3968,24 @@ or
<ref table="Advertised_Route" db="OVN_Southbound"/> table.
</p>
+ <p>
+ If <code>nb</code> is in the list then northd will create entries in
+ <ref table="Advertised_Route" db="OVN_Southbound"/> table for each
+ Load Balancer VIP on this port's router, and it's neighboring
+ routers. Neighboring routers are those that are either directly
+ connected to this Logical Router Port, or those that are connected
+ via shared Logical Switch.
+ </p>
+
+ <p>
+ If <code>nat</code> is in the list then northd will create entries in
+ <ref table="Advertised_Route" db="OVN_Southbound"/> table for each
+ NAT's external IP on this port's router, and it's neighboring
+ routers. Neighboring routers are those that are either directly
+ connected to this Logical Router Port, or those that are connected
+ via shared Logical Switch.
+ </p>
+
<p>
If not set the value from <ref column="options"
key="dynamic-routing-redistribute" table="Logical_Router"/> on the
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 64991ff75..c59512b29 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -15542,3 +15542,604 @@ CHECK_NO_CHANGE_AFTER_RECOMPUTE
AT_CLEANUP
])
+
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([dynamic-routing - nat sync to sb IPv4])
+AT_KEYWORDS([dynamic-routing])
+ovn_start
+
+# Start with GW router and a single LRP
+check ovn-nbctl lr-add lr0
+check ovn-nbctl \
+ -- \
+ set Logical_Router lr0 options:dynamic-routing=true \
+ options:chassis=hv1
+check ovn-nbctl --wait=sb \
+ -- \
+ lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
+
+check_row_count Advertised_Route 0
+
+datapath=$(ovn-sbctl --bare --columns _uuid list datapath_binding lr0)
+pb=$(ovn-sbctl --bare --columns _uuid list port_binding lr0-sw0)
+
+# Adding LRP dynamic-routing-nat option and NAT rule advertises a route entry
+check ovn-nbctl --wait=sb \
+ -- \
+ lrp-set-options lr0-sw0 dynamic-routing-redistribute=nat \
+ -- \
+ lr-nat-add lr0 dnat_and_snat 172.16.1.10 192.168.1.10
+
+ovn-nbctl list NAT
+ovn-sbctl list Advertised_Route
+ovn-sbctl lflow-list
+
+check_row_count Advertised_Route 1
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb], [0], [dnl
+172.16.1.10
+])
+
+# Add LR with distributed LRP connected to GW router through join LS
+# and switch dynamic-routing-redistribute to lrp-guest-join so that
+# it advertises NAT routes from the neighbors in the join LS
+check ovn-nbctl --wait=sb \
+ -- \
+ remove Logical_Router_Port lr0-sw0 options dynamic-routing-redistribute \
+ -- \
+ lrp-add lr0 lr0-join 00:00:00:00:ff:02 10.42.0.1/24 \
+ -- \
+ lrp-set-options lr0-join dynamic-routing-redistribute=nat \
+ -- \
+ ls-add ls-join \
+ -- \
+ lsp-add ls-join lsp-join-to-lr0 \
+ -- \
+ lsp-set-type lsp-join-to-lr0 router \
+ -- \
+ lsp-set-options lsp-join-to-lr0 router-port=lr0-join \
+ -- \
+ lsp-set-addresses lsp-join-to-lr0 router \
+ -- \
+ lr-add lr-guest0 \
+ -- \
+ lrp-add lr-guest0 lrp-guest0-sw0 00:00:00:00:fe:01 10.51.0.1/24 \
+ -- \
+ lrp-add lr-guest0 lrp-guest0-join 00:00:00:00:fe:02 10.42.0.2/24 \
+ -- \
+ lsp-add ls-join lsp-join-to-guest0 \
+ -- \
+ lsp-set-type lsp-join-to-guest0 router \
+ -- \
+ lsp-set-options lsp-join-to-guest0 router-port=lrp-guest0-join \
+ -- \
+ lrp-set-gateway-chassis lrp-guest0-join hv1
+
+pb=$(ovn-sbctl --bare --columns _uuid list port_binding lr0-join)
+pb2=$(ovn-sbctl --bare --columns _uuid list port_binding lrp-guest0-join)
+
+check ovn-nbctl --wait=sb \
+ --add-route lr-nat-add lr-guest0 dnat_and_snat 172.16.2.10 192.168.2.10
+
+check_row_count Advertised_Route 2
+ovn-sbctl list advertised_route
+# Routes to local NAT addresses are advertised without tracked port
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port="$pb" tracked_port=[[]]], [0], [dnl
+172.16.1.10
+])
+# Routes to NAT addresses of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port="$pb" tracked_port="$pb2"], [0], [dnl
+172.16.2.10
+])
+
+# Add nonlocal LR with distributed LRP connected to GW router through join LS
+check ovn-nbctl --wait=sb \
+ -- \
+ lr-add lr-guest1 \
+ -- \
+ lrp-add lr-guest1 lrp-guest1-sw0 00:00:00:00:fd:01 10.51.1.1/24 \
+ -- \
+ lrp-add lr-guest1 lrp-guest1-join 00:00:00:00:fd:02 10.42.0.3/24 \
+ -- \
+ lrp-set-options lrp-guest1-join dynamic-routing-nat=true \
+ -- \
+ lsp-add ls-join lsp-join-to-guest1 \
+ -- \
+ lsp-set-type lsp-join-to-guest1 router \
+ -- \
+ lsp-set-options lsp-join-to-guest1 router-port=lrp-guest1-join \
+ -- \
+ lrp-set-gateway-chassis lrp-guest1-join nonlocalhv
+
+pb3=$(ovn-sbctl --bare --columns _uuid list port_binding lrp-guest1-join)
+
+check ovn-nbctl --wait=sb \
+ --add-route lr-nat-add lr-guest1 dnat_and_snat 172.16.3.10 192.168.3.10
+check_row_count Advertised_Route 3
+# Routes to local NAT addresses are advertised without tracked port
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port=[[]]], [0], [dnl
+172.16.1.10
+])
+# Routes to NAT addresses of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port=$pb2], [0], [dnl
+172.16.2.10
+])
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port=$pb3], [0], [dnl
+172.16.3.10
+])
+
+# removing the option:dynamic-routing removes all routes
+check ovn-nbctl --wait=sb remove Logical_Router lr0 option dynamic-routing
+check_row_count Advertised_Route 0
+
+# and setting it again adds them again
+check ovn-nbctl --wait=sb set Logical_Router lr0 option:dynamic-routing=true
+check_row_count Advertised_Route 3
+
+# removing the lr will remove all routes
+check ovn-nbctl --wait=sb lr-del lr0
+check_row_count Advertised_Route 0
+
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([dynamic-routing - nat sync to sb IPv6])
+AT_KEYWORDS([dynamic-routing])
+ovn_start
+
+# Start with GW router and a single LRP
+check ovn-nbctl lr-add lr0
+check ovn-nbctl \
+ -- \
+ set Logical_Router lr0 options:dynamic-routing=true \
+ options:chassis=hv1
+check ovn-nbctl --wait=sb \
+ -- \
+ lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 2001:db0::1/64
+
+check_row_count Advertised_Route 0
+
+datapath=$(ovn-sbctl --bare --columns _uuid list datapath_binding lr0)
+pb=$(ovn-sbctl --bare --columns _uuid list port_binding lr0-sw0)
+
+# Adding LRP dynamic-routing-nat option and NAT rule advertises a route entry
+check ovn-nbctl --wait=sb \
+ -- \
+ lrp-set-options lr0-sw0 dynamic-routing-redistribute=nat \
+ -- \
+ lr-nat-add lr0 dnat_and_snat 2001:db1::10 2001:db2::10
+
+ovn-nbctl list NAT
+ovn-sbctl list Advertised_Route
+ovn-sbctl lflow-list
+
+check_row_count Advertised_Route 1
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb], [0], [dnl
+2001:db1::10
+])
+
+# Add LR with distributed LRP connected to GW router through join LS
+# and switch dynamic-routing-redistribute to lrp-guest-join so that
+# it advertises NAT routes from the neighbors in the join LS
+check ovn-nbctl --wait=sb \
+ -- \
+ remove Logical_Router_Port lr0-sw0 options dynamic-routing-redistribute \
+ -- \
+ lrp-add lr0 lr0-join 00:00:00:00:ff:02 2001:db42::1/64 \
+ -- \
+ lrp-set-options lr0-join dynamic-routing-redistribute=nat \
+ -- \
+ ls-add ls-join \
+ -- \
+ lsp-add ls-join lsp-join-to-lr0 \
+ -- \
+ lsp-set-type lsp-join-to-lr0 router \
+ -- \
+ lsp-set-options lsp-join-to-lr0 router-port=lr0-join \
+ -- \
+ lsp-set-addresses lsp-join-to-lr0 router \
+ -- \
+ lr-add lr-guest0 \
+ -- \
+ lrp-add lr-guest0 lrp-guest0-sw0 00:00:00:00:fe:01 2001:db51::1/64 \
+ -- \
+ lrp-add lr-guest0 lrp-guest0-join 00:00:00:00:fe:02 2001:db42::2/64 \
+ -- \
+ lrp-set-options lrp-guest0-join dynamic-routing-nat=true \
+ -- \
+ lsp-add ls-join lsp-join-to-guest0 \
+ -- \
+ lsp-set-type lsp-join-to-guest0 router \
+ -- \
+ lsp-set-options lsp-join-to-guest0 router-port=lrp-guest0-join \
+ -- \
+ lrp-set-gateway-chassis lrp-guest0-join hv1
+
+pb=$(ovn-sbctl --bare --columns _uuid list port_binding lr0-join)
+pb2=$(ovn-sbctl --bare --columns _uuid list port_binding lrp-guest0-join)
+
+check ovn-nbctl --wait=sb \
+ --add-route lr-nat-add lr-guest0 dnat_and_snat 2001:db3::10 2001:db4::10
+
+check_row_count Advertised_Route 2
+ovn-sbctl list advertised_route
+# Routes to local NAT addresses are advertised without tracked port
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port="$pb" tracked_port=[[]]], [0], [dnl
+2001:db1::10
+])
+# Routes to NAT addresses of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port=$pb2], [0], [dnl
+2001:db3::10
+])
+
+# Add nonlocal LR with distributed LRP connected to GW router through join LS
+check ovn-nbctl --wait=sb \
+ -- \
+ lr-add lr-guest1 \
+ -- \
+ lrp-add lr-guest1 lrp-guest1-sw0 00:00:00:00:fd:01 2001:db52::1/64 \
+ -- \
+ lrp-add lr-guest1 lrp-guest1-join 00:00:00:00:fd:02 2001:db42::3/64 \
+ -- \
+ lrp-set-options lrp-guest1-join dynamic-routing-nat=true \
+ -- \
+ lsp-add ls-join lsp-join-to-guest1 \
+ -- \
+ lsp-set-type lsp-join-to-guest1 router \
+ -- \
+ lsp-set-options lsp-join-to-guest1 router-port=lrp-guest1-join \
+ -- \
+ lrp-set-gateway-chassis lrp-guest1-join nonlocalhv
+
+pb3=$(ovn-sbctl --bare --columns _uuid list port_binding lrp-guest1-join)
+
+check ovn-nbctl --wait=sb \
+ --add-route lr-nat-add lr-guest1 dnat_and_snat 2001:db5::10 2001:db6::10
+check_row_count Advertised_Route 3
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port=[[]]], [0], [dnl
+2001:db1::10
+])
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port=$pb2], [0], [dnl
+2001:db3::10
+])
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port=$pb3], [0], [dnl
+2001:db5::10
+])
+
+# removing the option:dynamic-routing removes all routes
+check ovn-nbctl --wait=sb remove Logical_Router lr0 option dynamic-routing
+check_row_count Advertised_Route 0
+
+# and setting it again adds them again
+check ovn-nbctl --wait=sb set Logical_Router lr0 option:dynamic-routing=true
+check_row_count Advertised_Route 3
+
+# removing the lr will remove all routes
+check ovn-nbctl --wait=sb lr-del lr0
+check_row_count Advertised_Route 0
+
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([dynamic-routing - LB sync to sb IPv4])
+AT_KEYWORDS([dynamic-routing])
+ovn_start
+
+# Start with GW router and a single LRP
+check ovn-nbctl lr-add lr0
+check ovn-nbctl \
+ -- \
+ set Logical_Router lr0 options:dynamic-routing=true \
+ options:chassis=hv1
+check ovn-nbctl --wait=sb \
+ -- \
+ lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
+
+check_row_count Advertised_Route 0
+
+datapath=$(ovn-sbctl --bare --columns _uuid list datapath_binding lr0)
+pb=$(ovn-sbctl --bare --columns _uuid list port_binding lr0-sw0)
+
+# Adding LRP dynamic-routing-lb-vips option and LB VIP rule advertises a route
entry
+check ovn-nbctl --wait=sb \
+ -- \
+ lrp-set-options lr0-sw0 dynamic-routing-redistribute=lb \
+ -- \
+ lb-add lb0 172.16.1.10:80 192.168.1.10:80,192.168.1.11:80 \
+ -- \
+ lr-lb-add lr0 lb0
+
+ovn-nbctl list Load_Balancer
+ovn-sbctl list Advertised_Route
+ovn-sbctl lflow-list
+
+check_row_count Advertised_Route 1
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb], [0], [dnl
+172.16.1.10
+])
+
+# Add LR with distributed LRP connected to GW router through join LS
+# and switch dynamic-routing-redistribute to lrp-guest-join so that
+# it advertises LB VIP routes from the neighbors in the join LS
+check ovn-nbctl --wait=sb \
+ -- \
+ remove Logical_Router_Port lr0-sw0 options dynamic-routing-redistribute \
+ -- \
+ lrp-add lr0 lr0-join 00:00:00:00:ff:02 10.42.0.1/24 \
+ -- \
+ lrp-set-options lr0-join dynamic-routing-redistribute=lb \
+ -- \
+ ls-add ls-join \
+ -- \
+ lsp-add ls-join lsp-join-to-lr0 \
+ -- \
+ lsp-set-type lsp-join-to-lr0 router \
+ -- \
+ lsp-set-options lsp-join-to-lr0 router-port=lr0-join \
+ -- \
+ lsp-set-addresses lsp-join-to-lr0 router \
+ -- \
+ lr-add lr-guest0 \
+ -- \
+ lrp-add lr-guest0 lrp-guest0-sw0 00:00:00:00:fe:01 10.51.0.1/24 \
+ -- \
+ lrp-add lr-guest0 lrp-guest0-join 00:00:00:00:fe:02 10.42.0.2/24 \
+ -- \
+ lrp-set-options lrp-guest0-join dynamic-routing-lb-vips=true \
+ -- \
+ lsp-add ls-join lsp-join-to-guest0 \
+ -- \
+ lsp-set-type lsp-join-to-guest0 router \
+ -- \
+ lsp-set-options lsp-join-to-guest0 router-port=lrp-guest0-join \
+ -- \
+ lrp-set-gateway-chassis lrp-guest0-join hv1
+
+pb=$(ovn-sbctl --bare --columns _uuid list port_binding lr0-join)
+pb2=$(ovn-sbctl --bare --columns _uuid list port_binding lrp-guest0-join)
+
+check ovn-nbctl --wait=sb \
+ --add-route lb-add lb1 172.16.2.10:80 192.168.2.10:80,192.168.2.11:80 \
+ -- \
+ lr-lb-add lr-guest0 lb1
+
+check_row_count Advertised_Route 2
+ovn-sbctl list advertised_route
+# Routes to local LB VIPs are advertised without tracked port
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port="$pb" tracked_port=[[]]], [0], [dnl
+172.16.1.10
+])
+# Routes to LB VIPs of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port="$pb2"], [0], [dnl
+172.16.2.10
+])
+
+# Add nonlocal LR with distributed LRP connected to GW router through join LS
+check ovn-nbctl --wait=sb \
+ -- \
+ lr-add lr-guest1 \
+ -- \
+ lrp-add lr-guest1 lrp-guest1-sw0 00:00:00:00:fd:01 10.51.1.1/24 \
+ -- \
+ lrp-add lr-guest1 lrp-guest1-join 00:00:00:00:fd:02 10.42.0.3/24 \
+ -- \
+ lsp-add ls-join lsp-join-to-guest1 \
+ -- \
+ lsp-set-type lsp-join-to-guest1 router \
+ -- \
+ lsp-set-options lsp-join-to-guest1 router-port=lrp-guest1-join \
+ -- \
+ lrp-set-gateway-chassis lrp-guest1-join nonlocalhv
+
+pb3=$(ovn-sbctl --bare --columns _uuid list port_binding lrp-guest1-join)
+
+check ovn-nbctl --wait=sb \
+ --add-route lb-add lb2 172.16.3.10:80 192.168.3.10:80,192.168.3.11:80 \
+ -- \
+ lr-lb-add lr-guest1 lb2
+check_row_count Advertised_Route 3
+# Routes to local LB VIPs are advertised without tracked port
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port=[[]]], [0], [dnl
+172.16.1.10
+])
+# Routes to LB VIPs of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port=$pb2], [0], [dnl
+172.16.2.10
+])
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port=$pb3], [0], [dnl
+172.16.3.10
+])
+
+# removing the option:dynamic-routing removes all routes
+check ovn-nbctl --wait=sb remove Logical_Router lr0 option dynamic-routing
+check_row_count Advertised_Route 0
+
+# and setting it again adds them again
+check ovn-nbctl --wait=sb set Logical_Router lr0 option:dynamic-routing=true
+check_row_count Advertised_Route 3
+
+# removing the lr will remove all routes
+check ovn-nbctl --wait=sb lr-del lr0
+check_row_count Advertised_Route 0
+
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([dynamic-routing - LB sync to sb IPv6])
+AT_KEYWORDS([dynamic-routing])
+ovn_start
+
+# Start with GW router and a single LRP
+check ovn-nbctl lr-add lr0
+check ovn-nbctl \
+ -- \
+ set Logical_Router lr0 options:dynamic-routing=true \
+ options:chassis=hv1
+check ovn-nbctl --wait=sb \
+ -- \
+ lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 2001:db0::1/64
+
+check_row_count Advertised_Route 0
+
+datapath=$(ovn-sbctl --bare --columns _uuid list datapath_binding lr0)
+pb=$(ovn-sbctl --bare --columns _uuid list port_binding lr0-sw0)
+
+# Adding LRP dynamic-routing-lb-vips option and LB VIP rule advertises a route
entry
+check ovn-nbctl --wait=sb \
+ -- \
+ lrp-set-options lr0-sw0 dynamic-routing-redistribute=lb \
+ -- \
+ lb-add lb0 [[2001:db1::10]]:80 [[2001:db2::10]]:80,[[2001:db2::11]]:80 \
+ -- \
+ lr-lb-add lr0 lb0
+
+ovn-nbctl list Load_Balancer
+ovn-sbctl list Advertised_Route
+ovn-sbctl lflow-list
+
+check_row_count Advertised_Route 1
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb], [0], [dnl
+2001:db1::10
+])
+
+# Add LR with distributed LRP connected to GW router through join LS
+# and switch dynamic-routing-redistribute to lrp-guest-join so that
+# it advertises LB VIP routes from the neighbors in the join LS
+check ovn-nbctl --wait=sb \
+ -- \
+ remove Logical_Router_Port lr0-sw0 options dynamic-routing-redistribute \
+ -- \
+ lrp-add lr0 lr0-join 00:00:00:00:ff:02 2001:db42::1/64 \
+ -- \
+ lrp-set-options lr0-join dynamic-routing-redistribute=lb \
+ -- \
+ ls-add ls-join \
+ -- \
+ lsp-add ls-join lsp-join-to-lr0 \
+ -- \
+ lsp-set-type lsp-join-to-lr0 router \
+ -- \
+ lsp-set-options lsp-join-to-lr0 router-port=lr0-join \
+ -- \
+ lsp-set-addresses lsp-join-to-lr0 router \
+ -- \
+ lr-add lr-guest0 \
+ -- \
+ lrp-add lr-guest0 lrp-guest0-sw0 00:00:00:00:fe:01 2001:db52::1/64 \
+ -- \
+ lrp-add lr-guest0 lrp-guest0-join 00:00:00:00:fe:02 2001:db42::2/64 \
+ -- \
+ lrp-set-options lrp-guest0-join dynamic-routing-lb-vips=true \
+ -- \
+ lsp-add ls-join lsp-join-to-guest0 \
+ -- \
+ lsp-set-type lsp-join-to-guest0 router \
+ -- \
+ lsp-set-options lsp-join-to-guest0 router-port=lrp-guest0-join \
+ -- \
+ lrp-set-gateway-chassis lrp-guest0-join hv1
+
+pb=$(ovn-sbctl --bare --columns _uuid list port_binding lr0-join)
+pb2=$(ovn-sbctl --bare --columns _uuid list port_binding lrp-guest0-join)
+
+check ovn-nbctl --wait=sb \
+ --add-route lb-add lb1 [[2001:db3::10]]:80
[[2001:db4::10]]:80,[[2001:db4::11]]:80 \
+ -- \
+ lr-lb-add lr-guest0 lb1
+
+check_row_count Advertised_Route 2
+ovn-sbctl list advertised_route
+# Routes to local LB VIPs are advertised without tracked port
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port="$pb" tracked_port=[[]]], [0], [dnl
+2001:db1::10
+])
+# Routes to LB VIPs of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port="$pb2"], [0], [dnl
+2001:db3::10
+])
+
+# Add nonlocal LR with distributed LRP connected to GW router through join LS
+check ovn-nbctl --wait=sb \
+ -- \
+ lr-add lr-guest1 \
+ -- \
+ lrp-add lr-guest1 lrp-guest1-sw0 00:00:00:00:fd:01 2001:db2::1/64 \
+ -- \
+ lrp-add lr-guest1 lrp-guest1-join 00:00:00:00:fd:02 2001:db42::3/64 \
+ -- \
+ lrp-set-options lrp-guest1-join dynamic-routing-lb-vips=true \
+ -- \
+ lsp-add ls-join lsp-join-to-guest1 \
+ -- \
+ lsp-set-type lsp-join-to-guest1 router \
+ -- \
+ lsp-set-options lsp-join-to-guest1 router-port=lrp-guest1-join \
+ -- \
+ lrp-set-gateway-chassis lrp-guest1-join nonlocalhv
+
+pb3=$(ovn-sbctl --bare --columns _uuid list port_binding lrp-guest1-join)
+
+check ovn-nbctl --wait=sb \
+ --add-route lb-add lb2 [[2001:db5::10]]:80
[[2001:db4::10]]:80,[[2001:db4::11]]:80 \
+ -- \
+ lr-lb-add lr-guest1 lb2
+check_row_count Advertised_Route 3
+# Routes to local LB VIPs are advertised without tracked port
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port=[[]]], [0], [dnl
+2001:db1::10
+])
+# Routes to LB VIPs of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port=$pb2], [0], [dnl
+2001:db3::10
+])
+AT_CHECK([ovn-sbctl --columns ip_prefix --bare find Advertised_Route \
+datapath=$datapath logical_port=$pb tracked_port=$pb3], [0], [dnl
+2001:db5::10
+])
+
+# removing the option:dynamic-routing removes all routes
+check ovn-nbctl --wait=sb remove Logical_Router lr0 option dynamic-routing
+check_row_count Advertised_Route 0
+
+# and setting it again adds them again
+check ovn-nbctl --wait=sb set Logical_Router lr0 option:dynamic-routing=true
+check_row_count Advertised_Route 3
+
+# removing the lr will remove all routes
+check ovn-nbctl --wait=sb lr-del lr0
+check_row_count Advertised_Route 0
+
+AT_CLEANUP
+])
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index d24e50974..1d6f72b80 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -16561,3 +16561,631 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d
/.*terminating with signal 15.*/d"])
AT_CLEANUP
])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([dynamic-routing - LB VIPs IPv4])
+AT_KEYWORDS([dynamic-routing])
+
+CHECK_VRF()
+CHECK_CONNTRACK()
+CHECK_CONNTRACK_NAT()
+
+vrf=1000
+VRF_RESERVE([$vrf])
+ovn_start
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-int])
+ADD_BR([br-ext], [set Bridge br-ext fail-mode=standalone])
+
+# Set external-ids in br-int needed for ovn-controller
+ovs-vsctl \
+ -- set Open_vSwitch . external-ids:system-id=hv1 \
+ -- set Open_vSwitch .
external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
+
+# Start ovn-controller
+start_daemon ovn-controller
+
+ovn-appctl vlog/set route_exchange
+check ovn-nbctl -- lr-add R1 \
+ -- set Logical_Router R1 options:requested-tnl-key=$vrf
options:dynamic-routing=true
+
+check ovn-nbctl ls-add sw0
+check ovn-nbctl ls-add public
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP], [1])
+
+check ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24
+check ovn-nbctl -- lrp-add R1 rp-public 00:00:02:01:02:03 172.16.1.1/24 \
+ -- lrp-set-options rp-public \
+ dynamic-routing-maintain-vrf=true \
+ dynamic-routing-redistribute=lb
+
+check ovn-nbctl set logical_router R1 options:chassis=hv1
+
+check ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \
+ type=router options:router-port=rp-sw0 \
+ -- lsp-set-addresses sw0-rp router
+
+check ovn-nbctl lsp-add public public-rp -- set Logical_Switch_Port public-rp \
+ type=router options:router-port=rp-public \
+ -- lsp-set-addresses public-rp router
+
+check ovs-vsctl set Open_vSwitch .
external-ids:ovn-bridge-mappings=phynet:br-ext
+
+check ovn-nbctl lsp-add public public1 \
+ -- lsp-set-addresses public1 unknown \
+ -- lsp-set-type public1 localnet \
+ -- lsp-set-options public1 network_name=phynet
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([test `ip route show table $vrf | wc -l` -eq 1], [1])
+
+
+# Create a load balancer and associate to R1
+check ovn-nbctl lb-add lb1 172.16.1.150:80 172.16.1.100:80
+check ovn-nbctl lr-lb-add R1 lb1
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP])
+
+OVS_WAIT_UNTIL_EQUAL([ip route list vrf ovnvrf$vrf | awk '{$1=$1};1'], [dnl
+blackhole 172.16.1.150 proto 84 metric 1000])
+
+# Add distributed router connected through "join" LS and ensure
+# that its LB VIPs are redistributed by R1. LS join has no
+# IP config, routers reach each other over IPv6 LLAs
+check ovn-nbctl --wait=sb \
+ -- \
+ remove Logical_Router_Port rp-public options dynamic-routing-redistribute \
+ -- \
+ lrp-add R1 r1-join 00:00:00:00:ff:02 \
+ -- \
+ lrp-set-options r1-join dynamic-routing-redistribute=lb \
+ -- \
+ ls-add ls-join \
+ -- \
+ lsp-add ls-join lsp-join-to-r1 \
+ -- \
+ lsp-set-type lsp-join-to-r1 router \
+ -- \
+ lsp-set-options lsp-join-to-r1 router-port=r1-join \
+ -- \
+ lsp-set-addresses lsp-join-to-r1 router \
+ -- \
+ lr-add R2 \
+ -- \
+ lrp-add R2 r2-join 00:00:00:00:fe:02 \
+ -- \
+ lsp-add ls-join lsp-join-to-r2 \
+ -- \
+ lsp-set-type lsp-join-to-r2 router \
+ -- \
+ lsp-set-options lsp-join-to-r2 router-port=r2-join \
+ -- \
+ lrp-set-gateway-chassis r2-join hv1
+
+# Create a load balancer and associate to R2
+check ovn-nbctl lb-add lb2 10.42.10.10:80 192.168.123.10:80
+check ovn-nbctl lr-lb-add R2 lb2
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP])
+
+OVS_WAIT_UNTIL_EQUAL([ip route list vrf ovnvrf$vrf | awk '{$1=$1};1'], [dnl
+blackhole 10.42.10.10 proto 84 metric 100
+blackhole 172.16.1.150 proto 84 metric 1000])
+
+# Move DGW of R2 to another chassis to verify that route priority will decrease
+check ovn-nbctl --wait=hv \
+ -- \
+ lrp-del-gateway-chassis r2-join hv1
+ -- \
+ lrp-set-gateway-chassis r2-join hv123
+
+OVS_WAIT_UNTIL_EQUAL([ip route list vrf ovnvrf$vrf | awk '{$1=$1};1'], [dnl
+blackhole 10.42.10.10 proto 84 metric 1000
+blackhole 172.16.1.150 proto 84 metric 1000])
+
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+
+# Ensure system resources are cleaned up
+AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP], [1])
+AT_CHECK([test `ip route show table $vrf | wc -l` -eq 1], [1])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/Failed to acquire.*/d
+/connection dropped.*/d"])
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([dynamic-routing - LB VIPs on gateway router IPv6])
+AT_KEYWORDS([dynamic-routing])
+
+CHECK_VRF()
+CHECK_CONNTRACK()
+CHECK_CONNTRACK_NAT()
+
+vrf=1001
+VRF_RESERVE([$vrf])
+ovn_start
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-int])
+ADD_BR([br-ext], [set Bridge br-ext fail-mode=standalone])
+
+# Set external-ids in br-int needed for ovn-controller
+ovs-vsctl \
+ -- set Open_vSwitch . external-ids:system-id=hv1 \
+ -- set Open_vSwitch .
external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
+
+# Start ovn-controller
+start_daemon ovn-controller
+
+ovn-appctl vlog/set route_exchange
+check ovn-nbctl -- lr-add R1 \
+ -- set Logical_Router R1 options:requested-tnl-key=$vrf
options:dynamic-routing=true
+
+check ovn-nbctl ls-add sw0
+check ovn-nbctl ls-add public
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP], [1])
+
+check ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 2001:db8:100::1/64
+check ovn-nbctl -- lrp-add R1 rp-public 00:00:02:01:02:03 2001:db8:1001::1/64 \
+ -- lrp-set-options rp-public \
+ dynamic-routing-maintain-vrf=true \
+ dynamic-routing-redistribute=lb
+
+check ovn-nbctl set logical_router R1 options:chassis=hv1
+
+check ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \
+ type=router options:router-port=rp-sw0 \
+ -- lsp-set-addresses sw0-rp router
+
+check ovn-nbctl lsp-add public public-rp -- set Logical_Switch_Port public-rp \
+ type=router options:router-port=rp-public \
+ -- lsp-set-addresses public-rp router
+
+check ovs-vsctl set Open_vSwitch .
external-ids:ovn-bridge-mappings=phynet:br-ext
+
+check ovn-nbctl lsp-add public public1 \
+ -- lsp-set-addresses public1 unknown \
+ -- lsp-set-type public1 localnet \
+ -- lsp-set-options public1 network_name=phynet
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([test `ip -6 route show table $vrf | wc -l` -eq 1], [1])
+
+# Create a load balancer and associate to R1
+check ovn-nbctl lb-add lb1 [[2001:db8:1001::150]]:80 [[2001:db8:2001::100]]:80
+check ovn-nbctl lr-lb-add R1 lb1
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([ip link | grep -q ovnvrf1001:.*UP])
+AT_CHECK([test `ip -6 route show table 1001 | wc -l` -eq 1])
+
+OVS_WAIT_UNTIL_EQUAL([ip -6 route list vrf ovnvrf$vrf | awk '{$1=$1};1'], [dnl
+blackhole 2001:db8:1001::150 dev lo proto 84 metric 1000 pref medium])
+
+# Add distributed router connected through "join" LS and ensure
+# that its LB VIPs are redistributed by R1. LS join has no
+# IP config, routers reach each other over IPv6 LLAs
+check ovn-nbctl --wait=sb \
+ -- \
+ remove Logical_Router_Port rp-public options dynamic-routing-redistribute \
+ -- \
+ lrp-add R1 r1-join 00:00:00:00:ff:02 \
+ -- \
+ lrp-set-options r1-join dynamic-routing-redistribute=lb \
+ -- \
+ ls-add ls-join \
+ -- \
+ lsp-add ls-join lsp-join-to-r1 \
+ -- \
+ lsp-set-type lsp-join-to-r1 router \
+ -- \
+ lsp-set-options lsp-join-to-r1 router-port=r1-join \
+ -- \
+ lsp-set-addresses lsp-join-to-r1 router \
+ -- \
+ lr-add R2 \
+ -- \
+ lrp-add R2 r2-join 00:00:00:00:fe:02 \
+ -- \
+ lsp-add ls-join lsp-join-to-r2 \
+ -- \
+ lsp-set-type lsp-join-to-r2 router \
+ -- \
+ lsp-set-options lsp-join-to-r2 router-port=r2-join \
+ -- \
+ lrp-set-gateway-chassis r2-join hv1
+
+# Create a load balancer and associate to R2
+check ovn-nbctl lb-add lb2 [[2001:db8:3001::150]]:80 [[2001:db8:4001::100]]:80
+check ovn-nbctl lr-lb-add R2 lb2
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP])
+
+OVS_WAIT_UNTIL_EQUAL([ip -6 route list vrf ovnvrf$vrf | awk '{$1=$1};1'], [dnl
+blackhole 2001:db8:1001::150 dev lo proto 84 metric 1000 pref medium
+blackhole 2001:db8:3001::150 dev lo proto 84 metric 100 pref medium])
+
+# Move DGW of R2 to another chassis to verify that route priority will decrease
+check ovn-nbctl --wait=hv \
+ -- \
+ lrp-del-gateway-chassis r2-join hv1
+ -- \
+ lrp-set-gateway-chassis r2-join hv123
+
+OVS_WAIT_UNTIL_EQUAL([ip -6 route list vrf ovnvrf$vrf | awk '{$1=$1};1'], [dnl
+blackhole 2001:db8:1001::150 dev lo proto 84 metric 1000 pref medium
+blackhole 2001:db8:3001::150 dev lo proto 84 metric 1000 pref medium])
+
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+
+# Ensure system resources are cleaned up
+AT_CHECK([ip link | grep -q ovnvrf1001:.*UP], [1])
+AT_CHECK([test `ip -6 route show table 1001 | wc -l` -eq 1], [1])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/Failed to acquire.*/d
+/connection dropped.*/d"])
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([dynamic-routing - DNAT and DNAT_AND_SNAT on gateway router IPv4])
+AT_KEYWORDS([dynamic-routing])
+
+CHECK_VRF()
+CHECK_CONNTRACK()
+CHECK_CONNTRACK_NAT()
+
+vrf=1002
+VRF_RESERVE([$vrf])
+ovn_start
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-int])
+ADD_BR([br-ext], [set Bridge br-ext fail-mode=standalone])
+
+# Set external-ids in br-int needed for ovn-controller
+ovs-vsctl \
+ -- set Open_vSwitch . external-ids:system-id=hv1 \
+ -- set Open_vSwitch .
external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
+
+# Start ovn-controller
+start_daemon ovn-controller
+
+ovn-appctl vlog/set route_exchange
+check ovn-nbctl -- lr-add R1 \
+ -- set Logical_Router R1 options:requested-tnl-key=$vrf
options:dynamic-routing=true
+
+check ovn-nbctl ls-add sw0
+check ovn-nbctl ls-add public
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP], [1])
+
+check ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24
+check ovn-nbctl -- lrp-add R1 rp-public 00:00:02:01:02:03 172.16.1.1/24 \
+ -- lrp-set-options rp-public \
+ dynamic-routing-maintain-vrf=true \
+ dynamic-routing-redistribute=nat
+
+check ovn-nbctl set logical_router R1 options:chassis=hv1
+
+check ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \
+ type=router options:router-port=rp-sw0 \
+ -- lsp-set-addresses sw0-rp router
+
+check ovn-nbctl lsp-add public public-rp -- set Logical_Switch_Port public-rp \
+ type=router options:router-port=rp-public \
+ -- lsp-set-addresses public-rp router
+
+check ovs-vsctl set Open_vSwitch .
external-ids:ovn-bridge-mappings=phynet:br-ext
+
+check ovn-nbctl lsp-add public public1 \
+ -- lsp-set-addresses public1 unknown \
+ -- lsp-set-type public1 localnet \
+ -- lsp-set-options public1 network_name=phynet
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([test `ip route show table $vrf | wc -l` -eq 2], [1])
+
+# Create dnat_and_snat, dnat rules in R1
+check ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.10 192.168.1.10
+check ovn-nbctl lr-nat-add R1 dnat 172.16.1.11 192.168.1.11
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP])
+
+OVS_WAIT_UNTIL_EQUAL([ip route list vrf ovnvrf$vrf | awk '{$1=$1};1'], [dnl
+blackhole 172.16.1.10 proto 84 metric 1000
+blackhole 172.16.1.11 proto 84 metric 1000])
+
+# Add distributed router connected through "join" LS and ensure
+# that its external NAT IPs are redistributed by R1. LS join has no
+# IP config, routers reach each other over IPv6 LLAs
+check ovn-nbctl --wait=sb \
+ -- \
+ remove Logical_Router_Port rp-public options dynamic-routing-redistribute \
+ -- \
+ lrp-add R1 r1-join 00:00:00:00:ff:02 \
+ -- \
+ lrp-set-options r1-join dynamic-routing-redistribute=nat \
+ -- \
+ ls-add ls-join \
+ -- \
+ lsp-add ls-join lsp-join-to-r1 \
+ -- \
+ lsp-set-type lsp-join-to-r1 router \
+ -- \
+ lsp-set-options lsp-join-to-r1 router-port=r1-join \
+ -- \
+ lsp-set-addresses lsp-join-to-r1 router \
+ -- \
+ lr-add R2 \
+ -- \
+ lrp-add R2 r2-join 00:00:00:00:fe:02 \
+ -- \
+ lsp-add ls-join lsp-join-to-r2 \
+ -- \
+ lsp-set-type lsp-join-to-r2 router \
+ -- \
+ lsp-set-options lsp-join-to-r2 router-port=r2-join \
+ -- \
+ lrp-set-gateway-chassis r2-join hv1
+
+# Create NAT on R2
+check ovn-nbctl lr-nat-add R2 dnat_and_snat 10.42.10.10 192.168.1.10
+check ovn-nbctl lr-nat-add R2 dnat_and_snat 10.42.10.11 192.168.1.11
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP])
+
+OVS_WAIT_UNTIL_EQUAL([ip route list vrf ovnvrf$vrf | awk '{$1=$1};1'], [dnl
+blackhole 10.42.10.10 proto 84 metric 100
+blackhole 10.42.10.11 proto 84 metric 100
+blackhole 172.16.1.10 proto 84 metric 1000
+blackhole 172.16.1.11 proto 84 metric 1000])
+
+# Move DGW of R2 to another chassis to verify that route priority will decrease
+check ovn-nbctl --wait=hv \
+ -- \
+ lrp-del-gateway-chassis r2-join hv1
+ -- \
+ lrp-set-gateway-chassis r2-join hv123
+
+OVS_WAIT_UNTIL_EQUAL([ip route list vrf ovnvrf$vrf | awk '{$1=$1};1'], [dnl
+blackhole 10.42.10.10 proto 84 metric 1000
+blackhole 10.42.10.11 proto 84 metric 1000
+blackhole 172.16.1.10 proto 84 metric 1000
+blackhole 172.16.1.11 proto 84 metric 1000])
+
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+
+# Ensure system resources are cleaned up
+AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP], [1])
+AT_CHECK([test `ip route show table $vrf | wc -l` -eq 1], [1])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/Failed to acquire.*/d
+/connection dropped.*/d"])
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([dynamic-routing - DNAT and DNAT_AND_SNAT on gateway router IPv6])
+AT_KEYWORDS([dynamic-routing])
+
+CHECK_VRF()
+CHECK_CONNTRACK()
+CHECK_CONNTRACK_NAT()
+
+vrf=1003
+VRF_RESERVE([$vrf])
+ovn_start
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-int])
+ADD_BR([br-ext], [set Bridge br-ext fail-mode=standalone])
+
+# Set external-ids in br-int needed for ovn-controller
+ovs-vsctl \
+ -- set Open_vSwitch . external-ids:system-id=hv1 \
+ -- set Open_vSwitch .
external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
+
+# Start ovn-controller
+start_daemon ovn-controller
+
+ovn-appctl vlog/set route_exchange
+check ovn-nbctl -- lr-add R1 \
+ -- set Logical_Router R1 options:requested-tnl-key=$vrf
options:dynamic-routing=true
+
+check ovn-nbctl ls-add sw0
+check ovn-nbctl ls-add public
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP], [1])
+
+check ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 2001:db8:100::1/64
+check ovn-nbctl -- lrp-add R1 rp-public 00:00:02:01:02:03 2001:db8:1003::1/64 \
+ -- lrp-set-options rp-public \
+ dynamic-routing-maintain-vrf=true \
+ dynamic-routing-redistribute=nat
+
+check ovn-nbctl set logical_router R1 options:chassis=hv1
+
+check ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \
+ type=router options:router-port=rp-sw0 \
+ -- lsp-set-addresses sw0-rp router
+
+check ovn-nbctl lsp-add public public-rp -- set Logical_Switch_Port public-rp \
+ type=router options:router-port=rp-public \
+ -- lsp-set-addresses public-rp router
+
+check ovs-vsctl set Open_vSwitch .
external-ids:ovn-bridge-mappings=phynet:br-ext
+
+check ovn-nbctl lsp-add public public1 \
+ -- lsp-set-addresses public1 unknown \
+ -- lsp-set-type public1 localnet \
+ -- lsp-set-options public1 network_name=phynet
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([test `ip -6 route show table $vrf | wc -l` -eq 2], [1])
+
+# Create dnat_and_snat, dnat rules in R1
+check ovn-nbctl lr-nat-add R1 \
+ dnat_and_snat 2001:db8:1003::150 2001:db8:100::100
+check ovn-nbctl lr-nat-add R1 \
+ dnat 2001:db8:1003::151 2001:db8:100::100
+
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP])
+OVS_WAIT_UNTIL_EQUAL([ip -6 route list vrf ovnvrf$vrf | awk '{$1=$1};1'], [dnl
+blackhole 2001:db8:1003::150 dev lo proto 84 metric 1000 pref medium
+blackhole 2001:db8:1003::151 dev lo proto 84 metric 1000 pref medium])
+
+# Add distributed router connected through "join" LS and ensure
+# that its external NAT IPs are redistributed by R1. LS join has no
+# IP config, routers reach each other over IPv6 LLAs
+check ovn-nbctl --wait=sb \
+ -- \
+ remove Logical_Router_Port rp-public options dynamic-routing-redistribute \
+ -- \
+ lrp-add R1 r1-join 00:00:00:00:ff:02 \
+ -- \
+ lrp-set-options r1-join dynamic-routing-redistribute=nat \
+ -- \
+ ls-add ls-join \
+ -- \
+ lsp-add ls-join lsp-join-to-r1 \
+ -- \
+ lsp-set-type lsp-join-to-r1 router \
+ -- \
+ lsp-set-options lsp-join-to-r1 router-port=r1-join \
+ -- \
+ lsp-set-addresses lsp-join-to-r1 router \
+ -- \
+ lr-add R2 \
+ -- \
+ lrp-add R2 r2-join 00:00:00:00:fe:02 \
+ -- \
+ lsp-add ls-join lsp-join-to-r2 \
+ -- \
+ lsp-set-type lsp-join-to-r2 router \
+ -- \
+ lsp-set-options lsp-join-to-r2 router-port=r2-join \
+ -- \
+ lrp-set-gateway-chassis r2-join hv1
+
+# Create NAT on R2
+check ovn-nbctl lr-nat-add R2 \
+ dnat_and_snat 2001:db8:1004::150 2001:db8:201::100
+check ovn-nbctl lr-nat-add R2 \
+ dnat 2001:db8:1004::151 2001:db8:201::101
+
+check ovn-nbctl --wait=hv sync
+
+#AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP])
+
+OVS_WAIT_UNTIL_EQUAL([ip -6 route list vrf ovnvrf$vrf | awk '{$1=$1};1'], [dnl
+blackhole 2001:db8:1003::150 dev lo proto 84 metric 1000 pref medium
+blackhole 2001:db8:1003::151 dev lo proto 84 metric 1000 pref medium
+blackhole 2001:db8:1004::150 dev lo proto 84 metric 100 pref medium
+blackhole 2001:db8:1004::151 dev lo proto 84 metric 100 pref medium])
+
+# Move DGW of R2 to another chassis to verify that route priority will decrease
+check ovn-nbctl --wait=hv \
+ -- \
+ lrp-del-gateway-chassis r2-join hv1
+ -- \
+ lrp-set-gateway-chassis r2-join hv123
+
+OVS_WAIT_UNTIL_EQUAL([ip -6 route list vrf ovnvrf$vrf | awk '{$1=$1};1'], [dnl
+blackhole 2001:db8:1003::150 dev lo proto 84 metric 1000 pref medium
+blackhole 2001:db8:1003::151 dev lo proto 84 metric 1000 pref medium
+blackhole 2001:db8:1004::150 dev lo proto 84 metric 1000 pref medium
+blackhole 2001:db8:1004::151 dev lo proto 84 metric 1000 pref medium])
+
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+# Ensure system resources are cleaned up
+AT_CHECK([ip link | grep -q ovnvrf1003:.*UP], [1])
+AT_CHECK([test `ip -6 route show table 1003 | wc -l` -eq 2], [1])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/Failed to acquire.*/d
+/connection dropped.*/d"])
+AT_CLEANUP
+])
+
+
--
2.43.0
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev