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                          |  10 +
 lib/stopwatch-names.h             |   1 +
 northd/en-advertised-route-sync.c | 198 ++++++--
 northd/en-advertised-route-sync.h |   3 +
 northd/en-learned-route-sync.c    |   3 +-
 northd/inc-proc-northd.c          |   5 +
 northd/northd.c                   | 254 +++++++++-
 northd/northd.h                   |  34 +-
 ovn-nb.xml                        |  36 ++
 tests/ovn-northd.at               | 628 +++++++++++++++++++++++++
 tests/system-ovn.at               | 738 ++++++++++++++++++++++++++++++
 11 files changed, 1861 insertions(+), 49 deletions(-)

diff --git a/TODO.rst b/TODO.rst
index d75db3152..593b9a580 100644
--- a/TODO.rst
+++ b/TODO.rst
@@ -146,6 +146,9 @@ OVN To-do List
   * Add incremental processing for northd when the Learned_Route table changes.
     Currently en_lflow is fully recomputed whenever such a change happens.
 
+  * Add incremental processing of en_dynamic_routes for stateful configuration
+    changes.
+
   * The ovn-controller currently loads all Advertised_Route entries on startup.
     This is to prevent deleting our routes on restart. If we defer updating
     routes until we are sure to have loaded all necessary Advertised_Routes
@@ -156,3 +159,10 @@ 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..a984e70a8 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,75 @@ 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,
+                                &northd_data->ls_ports,
+                                &dynamic_routes_data->parsed_routes);
+        build_nat_connected_parsed_routes(od, &lr_stateful_data->table,
+                                          &northd_data->ls_ports,
+                                          &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);
+}
+
 struct ar_entry {
     struct hmap_node hmap_node;
 
@@ -333,12 +399,89 @@ 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) {
+            if (route->tracked_port->od->nbr) {
+                uuidset_insert(&data->nb_lr,
+                               &route->tracked_port->od->nbr->header_.uuid);
+            } else if (route->tracked_port->od->nbs) {
+                uuidset_insert(&data->nb_ls,
+                               &route->tracked_port->od->nbs->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 +489,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..0290f08d2 100644
--- a/northd/en-advertised-route-sync.h
+++ b/northd/en-advertised-route-sync.h
@@ -36,4 +36,7 @@ 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);
 
+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..e94e98f78 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,11 @@ 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, NULL);
+    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 1d9f0675e..1558654a0 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",
@@ -11000,6 +11008,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)
 {
 
@@ -11015,6 +11024,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);
@@ -11040,7 +11050,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;
@@ -11083,13 +11093,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);
 
@@ -11226,7 +11237,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);
 }
 
@@ -11244,7 +11255,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++) {
@@ -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);
     }
 }
 
@@ -11294,6 +11305,237 @@ 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,
+                                const struct hmap *ls_ports,
+                                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);
+
+        const struct ovn_port *tracked_port =
+            nat->is_distributed && nat->is_valid
+            ? ovn_port_find(ls_ports, nat->nb->logical_port)
+            : nat->l3dgw_port;
+        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_,
+                         tracked_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,
+                        const struct hmap *ls_ports,
+                        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, ls_ports, 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,
+    const struct hmap *ls_ports,
+    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,
+                                            ls_ports, 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,
+                                            ls_ports, 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 */
@@ -11468,6 +11710,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..1bb0cbd6f 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,20 @@ 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 *,
+                             const struct hmap *ls_ports,
+                             struct hmap *routes);
+void build_nat_connected_parsed_routes(const struct ovn_datapath *,
+                                       const struct lr_stateful_table *,
+                                       const struct hmap *ls_ports,
+                                       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..f10c1e738 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>lb</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>lb</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..a18321818 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -15542,3 +15542,631 @@ 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 lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 0
+
+datapath=$(fetch_column Datapath_Binding _uuid external_ids:name=lr0)
+pb=$(fetch_column Port_Binding _uuid logical_port=lr0-sw0)
+
+# Adding LRP dynamic-routing-redistribute=nat option and NAT rule advertises a
+# route entry.
+check ovn-nbctl \
+    -- lrp-set-options lr0-sw0 dynamic-routing-redistribute=nat \
+    -- lr-nat-add lr0 dnat_and_snat 172.16.1.10 192.168.1.10
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 1
+check_row_count Advertised_Route 1 \
+    ip_prefix="172.16.1.10"        \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=[[]]
+
+# 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 \
+    -- 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
+check ovn-nbctl --wait=sb sync
+
+pb=$(fetch_column Port_Binding _uuid logical_port=lr0-join)
+pb2=$(fetch_column Port_Binding _uuid logical_port=lrp-guest0-join)
+
+check ovn-nbctl \
+    --add-route lr-nat-add lr-guest0 dnat_and_snat 172.16.2.10 192.168.2.10
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 2
+
+# Routes to local NAT addresses are advertised without tracked port.
+check_row_count Advertised_Route 1 \
+    ip_prefix="172.16.1.10"        \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=[[]]
+
+# Routes to NAT addresses of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router.
+check_row_count Advertised_Route 1 \
+    ip_prefix="172.16.2.10"        \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=$pb2
+
+# Add nonlocal LR with distributed LRP connected to GW router through join LS.
+check ovn-nbctl \
+    -- 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
+check ovn-nbctl --wait=sb sync
+
+pb3=$(fetch_column Port_Binding _uuid logical_port=lrp-guest1-join)
+
+check ovn-nbctl \
+    -- --add-route lr-nat-add lr-guest1 dnat_and_snat 172.16.3.10 192.168.3.10
+check ovn-nbctl --wait=sb sync
+
+# Add a distributed router with distributed NAT connected to a different
+# public LS.
+check ovn-nbctl \
+    -- ls-add ls-pub \
+    -- lsp-add ls-pub ls-pub-ln \
+    -- lsp-set-type ls-pub-ln localnet \
+    -- lsp-set-addresses ls-pub-ln unknown \
+    -- lsp-set-options ls-pub-ln network_name=public \
+    -- lr-add dr \
+    -- set logical_router dr options:dynamic-routing=true \
+    -- lrp-add dr lrp-dr-sw1 00:00:00:00:fd:01 10.52.1.1/24 \
+    -- lrp-add dr lrp-dr-pub 00:00:00:00:fd:02 10.42.0.4/24 \
+    -- lrp-set-options lrp-dr-pub dynamic-routing-redistribute=nat \
+    -- lsp-add ls-pub lsp-pub-to-guest2 \
+    -- lsp-set-type lsp-pub-to-guest2 router \
+    -- lsp-set-options lsp-pub-to-guest2 router-port=lrp-dr-pub \
+    -- lrp-set-gateway-chassis lrp-dr-pub nonlocalhv \
+    -- ls-add sw1 \
+    -- lsp-add sw1 sw1-lrp-dr \
+    -- lsp-set-type sw1-lrp-dr router \
+    -- lsp-set-addresses sw1-lrp-dr router \
+    -- lsp-set-options sw1-lrp-dr router-port=lrp-dr-sw1 \
+    -- lsp-add sw1 vm
+check ovn-nbctl --wait=sb sync
+
+datapath_dr=$(fetch_column Datapath_Binding _uuid external_ids:name=dr)
+pb_dr=$(fetch_column Port_Binding _uuid logical_port=lrp-dr-pub)
+pb_vm=$(fetch_column Port_Binding _uuid logical_port=vm)
+
+check ovn-nbctl \
+    -- --add-route lr-nat-add dr dnat_and_snat 172.16.4.10 192.168.3.20 \
+       vm 00:00:00:00:fd:03
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 4
+
+# Routes to local NAT addresses are advertised without tracked port.
+check_row_count Advertised_Route 1 \
+    ip_prefix="172.16.1.10"        \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=[[]]
+
+# Routes to NAT addresses of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router.
+check_row_count Advertised_Route 1 \
+    ip_prefix="172.16.2.10"        \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=$pb2
+check_row_count Advertised_Route 1 \
+    ip_prefix="172.16.3.10"        \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=$pb3
+
+# Routes to distributed NAT addresses are advertised with tracked port set
+# to the logical_port of the NAT entry.
+check_row_count Advertised_Route 1 \
+    ip_prefix="172.16.4.10"        \
+    datapath=$datapath_dr          \
+    logical_port=$pb_dr            \
+    tracked_port=$pb_vm
+
+# 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 1
+
+check ovn-nbctl --wait=sb remove Logical_Router dr 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
+
+check ovn-nbctl --wait=sb set Logical_Router dr option:dynamic-routing=true
+check_row_count Advertised_Route 4
+
+# Removing the lr will remove all routes.
+check ovn-nbctl lr-del lr0 -- lr-del dr
+check ovn-nbctl --wait=sb sync
+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 lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 2001:db0::1/64
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 0
+
+datapath=$(fetch_column Datapath_Binding _uuid external_ids:name=lr0)
+pb=$(fetch_column Port_Binding _uuid logical_port=lr0-sw0)
+
+# Adding LRP dynamic-routing-redistribute=nat option and NAT rule advertises
+# a route entry.
+check ovn-nbctl \
+    -- lrp-set-options lr0-sw0 dynamic-routing-redistribute=nat \
+    -- lr-nat-add lr0 dnat_and_snat 2001:db1::10 2001:db2::10
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 1
+check_row_count Advertised_Route 1 \
+    ip_prefix="2001\:db1\:\:10"    \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=[[]]
+
+# 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 \
+    -- 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 \
+    -- 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
+check ovn-nbctl --wait=sb sync
+
+pb=$(fetch_column Port_Binding _uuid logical_port=lr0-join)
+pb2=$(fetch_column Port_Binding _uuid logical_port=lrp-guest0-join)
+
+check ovn-nbctl \
+    --add-route lr-nat-add lr-guest0 dnat_and_snat 2001:db3::10 2001:db4::10
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 2
+
+# Routes to local NAT addresses are advertised without tracked port.
+check_row_count Advertised_Route 1 \
+    ip_prefix="2001\:db1\:\:10"    \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=[[]]
+
+# Routes to NAT addresses of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router.
+check_row_count Advertised_Route 1 \
+    ip_prefix="2001\:db3\:\:10"    \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=$pb2
+
+# Add nonlocal LR with distributed LRP connected to GW router through join LS.
+check ovn-nbctl \
+    -- 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 \
+    -- 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
+check ovn-nbctl --wait=sb sync
+
+pb3=$(fetch_column Port_Binding _uuid logical_port=lrp-guest1-join)
+
+check ovn-nbctl \
+    --add-route lr-nat-add lr-guest1 dnat_and_snat 2001:db5::10 2001:db6::10
+check ovn-nbctl --wait=sb sync
+
+# Add a distributed router with distributed NAT connected to a different
+# public LS.
+check ovn-nbctl \
+    -- ls-add ls-pub \
+    -- lsp-add ls-pub ls-pub-ln \
+    -- lsp-set-type ls-pub-ln localnet \
+    -- lsp-set-addresses ls-pub-ln unknown \
+    -- lsp-set-options ls-pub-ln network_name=public \
+    -- lr-add dr \
+    -- set logical_router dr options:dynamic-routing=true \
+    -- lrp-add dr lrp-dr-sw1 00:00:00:00:fd:01 2001:db53::1/64 \
+    -- lrp-add dr lrp-dr-pub 00:00:00:00:fd:02 2001:db42::4/64 \
+    -- lrp-set-options lrp-dr-pub dynamic-routing-redistribute=nat \
+    -- lsp-add ls-pub lsp-pub-to-guest2 \
+    -- lsp-set-type lsp-pub-to-guest2 router \
+    -- lsp-set-options lsp-pub-to-guest2 router-port=lrp-dr-pub \
+    -- lrp-set-gateway-chassis lrp-dr-pub nonlocalhv \
+    -- ls-add sw1 \
+    -- lsp-add sw1 sw1-lrp-dr \
+    -- lsp-set-type sw1-lrp-dr router \
+    -- lsp-set-addresses sw1-lrp-dr router \
+    -- lsp-set-options sw1-lrp-dr router-port=lrp-dr-sw1 \
+    -- lsp-add sw1 vm
+check ovn-nbctl --wait=sb sync
+
+datapath_dr=$(fetch_column Datapath_Binding _uuid external_ids:name=dr)
+pb_dr=$(fetch_column Port_Binding _uuid logical_port=lrp-dr-pub)
+pb_vm=$(fetch_column Port_Binding _uuid logical_port=vm)
+
+check ovn-nbctl \
+    -- --add-route lr-nat-add dr dnat_and_snat 2001:db6::10 2001:db6::20 \
+       vm 00:00:00:00:fd:03
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 4
+
+# Routes to local NAT addresses are advertised without tracked port.
+check_row_count Advertised_Route 1 \
+    ip_prefix="2001\:db1\:\:10"    \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=[[]]
+
+# Routes to NAT addresses of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router.
+check_row_count Advertised_Route 1 \
+    ip_prefix="2001\:db3\:\:10"    \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=$pb2
+check_row_count Advertised_Route 1 \
+    ip_prefix="2001\:db5\:\:10"    \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=$pb3
+
+# Routes to distributed NAT addresses are advertised with tracked port set
+# to the logical_port of the NAT entry.
+check_row_count Advertised_Route 1 \
+    ip_prefix="2001\:db6\:\:10"    \
+    datapath=$datapath_dr          \
+    logical_port=$pb_dr            \
+    tracked_port=$pb_vm
+
+# 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 1
+
+check ovn-nbctl --wait=sb remove Logical_Router dr 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
+
+check ovn-nbctl --wait=sb set Logical_Router dr option:dynamic-routing=true
+check_row_count Advertised_Route 4
+
+# Removing the lr will remove all routes.
+check ovn-nbctl lr-del lr0 -- lr-del dr
+check ovn-nbctl --wait=sb sync
+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 lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 0
+
+datapath=$(fetch_column Datapath_Binding _uuid external_ids:name=lr0)
+pb=$(fetch_column Port_Binding _uuid logical_port=lr0-sw0)
+
+# Adding LRP dynamic-routing-lb-vips option and LB VIP rule advertises
+# a route entry.
+check ovn-nbctl 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
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 1
+check_row_count Advertised_Route 1 \
+    ip_prefix="172.16.1.10"        \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=[[]]
+
+# 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 \
+    -- 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
+check ovn-nbctl --wait=sb sync
+
+pb=$(fetch_column Port_Binding _uuid logical_port=lr0-join)
+pb2=$(fetch_column Port_Binding _uuid logical_port=lrp-guest0-join)
+
+check ovn-nbctl \
+    --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 ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 2
+
+# Routes to local LB VIPs are advertised without tracked port.
+check_row_count Advertised_Route 1 \
+    ip_prefix="172.16.1.10"        \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=[[]]
+
+# Routes to LB VIPs of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router.
+check_row_count Advertised_Route 1 \
+    ip_prefix="172.16.2.10"        \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=$pb2
+
+# Add nonlocal LR with distributed LRP connected to GW router through join LS.
+check ovn-nbctl \
+    -- 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
+check ovn-nbctl --wait=sb sync
+
+pb3=$(fetch_column Port_Binding _uuid logical_port=lrp-guest1-join)
+
+check ovn-nbctl \
+    -- --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 ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 3
+
+# Routes to local LB VIPs are advertised without tracked port.
+check_row_count Advertised_Route 1 \
+    ip_prefix="172.16.1.10"        \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=[[]]
+
+# Routes to LB VIPs of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router.
+check_row_count Advertised_Route 1 \
+    ip_prefix="172.16.2.10"        \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=$pb2
+check_row_count Advertised_Route 1 \
+    ip_prefix="172.16.3.10"        \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=$pb3
+
+# 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 lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 2001:db0::1/64
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 0
+
+datapath=$(fetch_column Datapath_Binding _uuid external_ids:name=lr0)
+pb=$(fetch_column Port_Binding _uuid logical_port=lr0-sw0)
+
+# Adding LRP dynamic-routing-lb-vips option and LB VIP rule advertises
+# a route entry.
+check ovn-nbctl 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
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 1
+check_row_count Advertised_Route 1 \
+    ip_prefix="2001\:db1\:\:10"    \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=[[]]
+
+# 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 \
+    -- 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
+check ovn-nbctl --wait=sb sync
+
+pb=$(fetch_column Port_Binding _uuid logical_port=lr0-join)
+pb2=$(fetch_column Port_Binding _uuid logical_port=lrp-guest0-join)
+
+check ovn-nbctl \
+    -- --add-route lb-add lb1 [[2001:db3::10]]:80 
[[2001:db4::10]]:80,[[2001:db4::11]]:80 \
+    -- lr-lb-add lr-guest0 lb1
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 2
+
+# Routes to local LB VIPs are advertised without tracked port.
+check_row_count Advertised_Route 1 \
+    ip_prefix="2001\:db1\:\:10"    \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=[[]]
+
+# Routes to LB VIPs of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router.
+check_row_count Advertised_Route 1 \
+    ip_prefix="2001\:db3\:\:10"    \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=$pb2
+
+# Add nonlocal LR with distributed LRP connected to GW router through join LS.
+check ovn-nbctl \
+    -- 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 \
+    -- 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
+check ovn-nbctl --wait=sb sync
+
+pb3=$(fetch_column Port_Binding _uuid logical_port=lrp-guest1-join)
+
+check ovn-nbctl \
+    -- --add-route lb-add lb2 [[2001:db5::10]]:80 
[[2001:db4::10]]:80,[[2001:db4::11]]:80 \
+    -- lr-lb-add lr-guest1 lb2
+check ovn-nbctl --wait=sb sync
+
+check_row_count Advertised_Route 3
+
+# Routes to local LB VIPs are advertised without tracked port.
+check_row_count Advertised_Route 1 \
+    ip_prefix="2001\:db1\:\:10"    \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=[[]]
+
+# Routes to LB VIPs of neighboring routers are advertised with
+# tracked port pointing to the LRP of the neighboring router.
+check_row_count Advertised_Route 1 \
+    ip_prefix="2001\:db3\:\:10"    \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=$pb2
+check_row_count Advertised_Route 1 \
+    ip_prefix="2001\:db5\:\:10"    \
+    datapath=$datapath             \
+    logical_port=$pb               \
+    tracked_port=$pb3
+
+# 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..d7b16deda 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -16561,3 +16561,741 @@ 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 \
+    -- \
+    ha-chassis-group-add g1 \
+    -- \
+    ha-chassis-group-add-chassis g1 hv1 10
+
+group_uuid=$(ovn-nbctl get ha_chassis_group g1 _uuid)
+check ovn-nbctl set logical_router_port r2-join ha_chassis_group="$group_uuid"
+
+# 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 \
+  -- \
+  ha-chassis-group-add-chassis g1 hv2 20 \
+  -- \
+  ha-chassis-group-remove-chassis g1 hv1
+
+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])
+
+# Add "guest" LS connected the distributed router R2 and one "VM" called
+# guest1.
+# Also, connect R2 to ls-join via nother DGW
+check ovn-nbctl --wait=sb \
+    -- \
+    lrp-add R2 r2-guest 00:00:00:00:fd:02 192.168.2.1/24 \
+    -- \
+    lrp-add R2 r2-join-dgw2 00:00:00:00:fd:02 \
+    -- \
+    lsp-add ls-join lsp-join-to-r2-dgw2 \
+    -- \
+    lsp-set-type lsp-join-to-r2-dgw2 router \
+    -- \
+    lsp-set-options lsp-join-to-r2-dgw2 router-port=r2-join-dgw2 \
+    -- \
+    ls-add ls-guest \
+    -- \
+    lsp-add ls-guest lsp-guest-to-r2 \
+    -- \
+    lsp-set-type lsp-guest-to-r2 router \
+    -- \
+    lsp-set-options lsp-guest-to-r2 router-port=r2-guest \
+    -- \
+    lsp-set-addresses lsp-guest-to-r2 router \
+    -- \
+    lsp-add ls-guest guest1 \
+    -- \
+    lsp-set-addresses guest1 '00:00:00:00:fc:03 192.168.2.10'
+
+check ovn-nbctl set logical_router_port r2-join-dgw2 \
+      ha_chassis_group="$group_uuid"
+# Bind guest1 on hv1
+ADD_NAMESPACES(guest1)
+ADD_VETH(guest1, guest1, br-int, "192.168.2.10/24", "00:00:00:00:fd:03", \
+         "192.168.2.1")
+
+# Add distributed NAT on R2 for guest1 and ensure that it's advertised
+# with better metric on chassis hv1 even when DGP for R2 is elsewhere.
+check ovn-nbctl --gateway-port \
+    r2-join-dgw2 --add-route lr-nat-add R2 dnat_and_snat 10.42.20.11 \
+    192.168.2.10 guest1 00:00:00:00:fc:04
+check ovn-nbctl --wait=hv sync
+
+ovn-sbctl list advertised_route
+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 10.42.20.11 proto 84 metric 100
+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 \
+    -- \
+    ha-chassis-group-add g1 \
+    -- \
+    ha-chassis-group-add-chassis g1 hv1 10
+
+group_uuid=$(ovn-nbctl get ha_chassis_group g1 _uuid)
+check ovn-nbctl set logical_router_port r2-join ha_chassis_group="$group_uuid"
+
+# 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 \
+  -- \
+  ha-chassis-group-add-chassis g1 hv2 20 \
+  -- \
+  ha-chassis-group-remove-chassis g1 hv1
+
+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])
+
+# Add "guest" LS connected the distributed router R2 and one "VM" called
+# guest1.
+# Also, connect R2 to ls-join via nother DGW
+check ovn-nbctl --wait=sb \
+    -- \
+    lrp-add R2 r2-guest 00:00:00:00:fd:02 2001:db9:2000::1/64 \
+    -- \
+    lrp-add R2 r2-join-dgw2 00:00:00:00:fd:02 \
+    -- \
+    lsp-add ls-join lsp-join-to-r2-dgw2 \
+    -- \
+    lsp-set-type lsp-join-to-r2-dgw2 router \
+    -- \
+    lsp-set-options lsp-join-to-r2-dgw2 router-port=r2-join-dgw2 \
+    -- \
+    ls-add ls-guest \
+    -- \
+    lsp-add ls-guest lsp-guest-to-r2 \
+    -- \
+    lsp-set-type lsp-guest-to-r2 router \
+    -- \
+    lsp-set-options lsp-guest-to-r2 router-port=r2-guest \
+    -- \
+    lsp-set-addresses lsp-guest-to-r2 router \
+    -- \
+    lsp-add ls-guest guest1 \
+    -- \
+    lsp-set-addresses guest1 '00:00:00:00:fc:03 2001:db9:2000::10'
+
+check ovn-nbctl set logical_router_port r2-join-dgw2 
ha_chassis_group="$group_uuid"
+# Bind guest1 on hv1
+ADD_NAMESPACES(guest1)
+ADD_VETH(guest1, guest1, br-int, "2001:db9:2000::10/64", "00:00:00:00:fd:03", \
+         "2001:db9:2000::1", "nodad")
+
+# Add distributed NAT on R2 for guest1 and ensure that it's advertised
+# with better metric on chassis hv1 even when DGP for R2 is elsewhere.
+check ovn-nbctl --gateway-port \
+    r2-join-dgw2 --add-route lr-nat-add R2 dnat_and_snat \
+    2001:db8:1005::150 2001:db9:2000::10 guest1 00:00:00:00:fc:04
+check ovn-nbctl --wait=hv sync
+
+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
+blackhole 2001:db8:1005::150 dev lo proto 84 metric 100 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

Reply via email to