get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/1.0/patches/2186653/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2186653,
    "url": "http://patchwork.ozlabs.org/api/1.0/patches/2186653/?format=api",
    "project": {
        "id": 68,
        "url": "http://patchwork.ozlabs.org/api/1.0/projects/68/?format=api",
        "name": "Open Virtual Network development",
        "link_name": "ovn",
        "list_id": "ovs-dev.openvswitch.org",
        "list_email": "ovs-dev@openvswitch.org",
        "web_url": "http://openvswitch.org/",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20260120114948.2289909-5-guilherme.paulo@luizalabs.com>",
    "date": "2026-01-20T11:49:43",
    "name": "[ovs-dev,v0,4/9] ovn-ic: Add a new engine-node 'route'.",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "aa04c33496b2191ae5c0d75eec8a9f52c26de7da",
    "submitter": {
        "id": 90256,
        "url": "http://patchwork.ozlabs.org/api/1.0/people/90256/?format=api",
        "name": "Paulo Guilherme Silva",
        "email": "guilherme.paulo@luizalabs.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/ovn/patch/20260120114948.2289909-5-guilherme.paulo@luizalabs.com/mbox/",
    "series": [
        {
            "id": 489037,
            "url": "http://patchwork.ozlabs.org/api/1.0/series/489037/?format=api",
            "date": "2026-01-20T11:49:40",
            "name": "Create multiple engines nodes for ovn-ic.",
            "version": 0,
            "mbox": "http://patchwork.ozlabs.org/series/489037/mbox/"
        }
    ],
    "check": "warning",
    "checks": "http://patchwork.ozlabs.org/api/patches/2186653/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<ovs-dev-bounces@openvswitch.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "dev@openvswitch.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@legolas.ozlabs.org",
            "ovs-dev@lists.linuxfoundation.org"
        ],
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=luizalabs.com header.i=@luizalabs.com\n header.a=rsa-sha256 header.s=google header.b=KIUcTb3n;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=2605:bc80:3010::137; helo=smtp4.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org)",
            "smtp4.osuosl.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key)\n header.d=luizalabs.com header.i=@luizalabs.com header.a=rsa-sha256\n header.s=google header.b=KIUcTb3n",
            "smtp4.osuosl.org; dmarc=pass (p=quarantine dis=none)\n header.from=luizalabs.com"
        ],
        "Received": [
            "from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4dwQcw2Jyjz1xsW\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 20 Jan 2026 22:50:40 +1100 (AEDT)",
            "from localhost (localhost [127.0.0.1])\n\tby smtp4.osuosl.org (Postfix) with ESMTP id A057D4EFC3;\n\tTue, 20 Jan 2026 11:50:38 +0000 (UTC)",
            "from smtp4.osuosl.org ([127.0.0.1])\n by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id jTOO42OvSXBI; Tue, 20 Jan 2026 11:50:31 +0000 (UTC)",
            "from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56])\n\tby smtp4.osuosl.org (Postfix) with ESMTPS id 57FAC4EF83;\n\tTue, 20 Jan 2026 11:50:31 +0000 (UTC)",
            "from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id 3AB80C02A6;\n\tTue, 20 Jan 2026 11:50:31 +0000 (UTC)",
            "from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137])\n by lists.linuxfoundation.org (Postfix) with ESMTP id 27A02C04FC\n for <dev@openvswitch.org>; Tue, 20 Jan 2026 11:50:29 +0000 (UTC)",
            "from localhost (localhost [127.0.0.1])\n by smtp4.osuosl.org (Postfix) with ESMTP id 5EE514EFA2\n for <dev@openvswitch.org>; Tue, 20 Jan 2026 11:50:28 +0000 (UTC)",
            "from smtp4.osuosl.org ([127.0.0.1])\n by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id FbZBbOT0Sytn for <dev@openvswitch.org>;\n Tue, 20 Jan 2026 11:50:25 +0000 (UTC)",
            "from mail-dy1-x1335.google.com (mail-dy1-x1335.google.com\n [IPv6:2607:f8b0:4864:20::1335])\n by smtp4.osuosl.org (Postfix) with ESMTPS id 8063A4EF8D\n for <dev@openvswitch.org>; Tue, 20 Jan 2026 11:50:25 +0000 (UTC)",
            "by mail-dy1-x1335.google.com with SMTP id\n 5a478bee46e88-2b6bfb0004aso7406377eec.0\n for <dev@openvswitch.org>; Tue, 20 Jan 2026 03:50:25 -0800 (PST)",
            "from WNLEC-CW22RF4.. ([177.75.155.81])\n by smtp.gmail.com with ESMTPSA id\n 5a478bee46e88-2b6b367cbc9sm18630559eec.32.2026.01.20.03.50.20\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 20 Jan 2026 03:50:22 -0800 (PST)"
        ],
        "X-Virus-Scanned": [
            "amavis at osuosl.org",
            "amavis at osuosl.org"
        ],
        "X-Comment": "SPF check N/A for local connections - client-ip=140.211.9.56;\n helo=lists.linuxfoundation.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN> ",
        "DKIM-Filter": [
            "OpenDKIM Filter v2.11.0 smtp4.osuosl.org 57FAC4EF83",
            "OpenDKIM Filter v2.11.0 smtp4.osuosl.org 8063A4EF8D"
        ],
        "Received-SPF": "Pass (mailfrom) identity=mailfrom;\n client-ip=2607:f8b0:4864:20::1335; helo=mail-dy1-x1335.google.com;\n envelope-from=guilherme.paulo@luizalabs.com; receiver=<UNKNOWN>",
        "DMARC-Filter": "OpenDMARC Filter v1.4.2 smtp4.osuosl.org 8063A4EF8D",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=luizalabs.com; s=google; t=1768909824; x=1769514624; darn=openvswitch.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=QG8wZTO72VeuemqhOrY9Fa6XegHnu70pOA+dmKWdHKM=;\n b=KIUcTb3nk1dDcUwW6lIYP/hLzcqKU6K9IZdpeGU+C/H+TDcwqK70Q1gh6hcReF7flr\n 8TvTQN4v/lsWDFb4Z0RRxlehqTZ9ldiREUNVsMurxojRBk4C2F6ZouyO37908cmMsxhg\n 26L3zTolfm4KKIXen8haFBINOcsJALlfXuQq0=",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20230601; t=1768909824; x=1769514624;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=QG8wZTO72VeuemqhOrY9Fa6XegHnu70pOA+dmKWdHKM=;\n b=uetsMhSxRzMnWJMf+JVqZ/KHKFRmfdvPghx414HiSrNiaJR122rpsQdbYHBIPOVZcP\n v2Km4H38ca4jyjVs4yVCxT4zqP76EF5QMEtbQ+TwiBLK4QMcFc2DzqaTwrSJXuVo6ovg\n I6anpUmi8uAZeMG6tkiJdX8bdCuQ8uLuKVEovsozBkFLgiYIAGnzn30krB9FUwhxD0oN\n bHg5/V68dVJpJ0RfMr/2tsYrJIPPIf1jV+whfoFP2+KLIM1BAdIBeqtKj1VuuzOp50xS\n Ew2xuIrowrGlwUm8Wc2knvhPbnYiqRso+KpvA2pmaUxBGZvCqiwx9qJ97THgA4h1LXfX\n E2KQ==",
        "X-Gm-Message-State": "AOJu0Ywp9WpYu1lLkR0lrZkcmx63Jxa8R8Pk59YDZP2w+CeQaL7q21l8\n MAxjut2w45hWoB5X8l/7WLqc0f3PMaHd0PNab+7EI4C0dxBW8zyRVxQnXdXi/ZjKM7525u3rQ1Y\n Yg0CUhbKC7liU6Z6SiEfO9KBQjFgPvhDs2v7hSmvmKjhO4/gQ4kbxi/b29pJ7",
        "X-Gm-Gg": "AZuq6aKhXV47Hk2KIR/zmUwJp/1gJrCq5F+UIiIvv+WJ9h4VkMm08kTj9hxbMhqtacR\n TKq+gyD0vtEPAQrtdZdhG6kJXMyFCUFU1OJt3DsFnZS0AWwi8fTLwmoQPVfdtZxnMemL/i8ko96\n HPaZl48ar587yIug+WYQzbL0Dw7g/4AejoaUTSJcOcaGsqSFaXvKTu8WVGa1x1yoaEetRPDlIoW\n 7F2pt+e8kwbXKX0B6R3wrKJQUS1778fcY7vT29N9DjlSRVwDOb4y/v5FmnqX1nirc/hGmALId7B\n s/EJEr2jTFD7NNI/Ef4W+Pym0XPmht9fKR91zWKoOyJrvoBH88aIfvnpTkgJpHxx/CDOIw/qj0L\n 5snQelYrNMacF4BvHPMBym5ot9vA9OXAHaeC0w26oiwbj2CGRQ0qdMMdrgtzVbWJllnWrmdiWrQ\n uMjuy7yhq0gtHO6eIqi2TFikQGVT4=",
        "X-Received": "by 2002:a05:7300:3b1a:b0:2ae:5ffa:8da4 with SMTP id\n 5a478bee46e88-2b6b3f1d5eamr9302531eec.1.1768909822766;\n Tue, 20 Jan 2026 03:50:22 -0800 (PST)",
        "To": "dev@openvswitch.org",
        "Date": "Tue, 20 Jan 2026 08:49:43 -0300",
        "Message-Id": "<20260120114948.2289909-5-guilherme.paulo@luizalabs.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20260120114948.2289909-1-guilherme.paulo@luizalabs.com>",
        "References": "<20260120114948.2289909-1-guilherme.paulo@luizalabs.com>",
        "MIME-Version": "1.0",
        "Subject": "[ovs-dev] [PATCH ovn v0 4/9] ovn-ic: Add a new engine-node 'route'.",
        "X-BeenThere": "ovs-dev@openvswitch.org",
        "X-Mailman-Version": "2.1.30",
        "Precedence": "list",
        "List-Id": "<ovs-dev.openvswitch.org>",
        "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>",
        "List-Archive": "<http://mail.openvswitch.org/pipermail/ovs-dev/>",
        "List-Post": "<mailto:ovs-dev@openvswitch.org>",
        "List-Help": "<mailto:ovs-dev-request@openvswitch.org?subject=help>",
        "List-Subscribe": "<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>",
        "From": "Paulo Guilherme Silva via dev <ovs-dev@openvswitch.org>",
        "Reply-To": "Paulo Guilherme Silva <guilherme.paulo@luizalabs.com>",
        "Content-Type": "text/plain; charset=\"iso-8859-1\"",
        "Content-Transfer-Encoding": "quoted-printable",
        "Errors-To": "ovs-dev-bounces@openvswitch.org",
        "Sender": "\"dev\" <ovs-dev-bounces@openvswitch.org>"
    },
    "content": "This new engine now maintains the route related data for ovn-ic\ndaemon which was earlier maintained by the ic\nengine node invoked the route_run() function. The inputs to\nthis engine node are:\n   en_nb_nb_global;\n   en_nb_logical_switch;\n   en_nb_logical_router;\n   en_icnb_transit_switch;\n   en_icsb_port_binding;\n   en_icsb_route;\n   en_nb_logical_router_static_route;\nIn order to achieve this, we refactor in the following way:\n* Introduce route_init() which initializes this data.\n* Introduce route_destroy() which clears this data for a new iteration.\n* Introduce route_run() which invokes the full recompute of the engine.\n\nThis engine node becomes an input to 'ic' node.\n\nSigned-off-by: Paulo Guilherme Silva <guilherme.paulo@luizalabs.com>\n---\n ic/automake.mk        |    2 +\n ic/en-ic.c            |   18 -\n ic/en-route.c         | 1554 +++++++++++++++++++++++++++++++++++++++++\n ic/en-route.h         |   70 ++\n ic/inc-proc-ic.c      |   15 +-\n ic/ovn-ic.c           | 1300 +---------------------------------\n ic/ovn-ic.h           |    8 +-\n lib/stopwatch-names.h |    1 +\n 8 files changed, 1646 insertions(+), 1322 deletions(-)\n create mode 100644 ic/en-route.c\n create mode 100644 ic/en-route.h",
    "diff": "diff --git a/ic/automake.mk b/ic/automake.mk\nindex 91783df0f..24807a360 100644\n--- a/ic/automake.mk\n+++ b/ic/automake.mk\n@@ -8,6 +8,8 @@ ic_ovn_ic_SOURCES = ic/ovn-ic.c \\\n \tic/en-enum-datapaths.h \\\n \tic/en-port-binding.c \\\n \tic/en-port-binding.h \\\n+\tic/en-route.c \\\n+\tic/en-route.h \\\n \tic/inc-proc-ic.c \\\n \tic/inc-proc-ic.h\n ic_ovn_ic_LDADD = \\\ndiff --git a/ic/en-ic.c b/ic/en-ic.c\nindex e7c7ab71b..e0956bdee 100644\n--- a/ic/en-ic.c\n+++ b/ic/en-ic.c\n@@ -44,8 +44,6 @@ ic_get_input_data(struct engine_node *node,\n                   struct ic_input *input_data)\n {\n     /* Table references */\n-    input_data->nbrec_nb_global_table =\n-        EN_OVSDB_GET(engine_get_input(\"NB_nb_global\", node));\n     input_data->nbrec_logical_switch_table =\n         EN_OVSDB_GET(engine_get_input(\"NB_logical_switch\", node));\n     input_data->nbrec_logical_router_table =\n@@ -112,22 +110,6 @@ ic_get_input_data(struct engine_node *node,\n         engine_ovsdb_node_get_index(\n             engine_get_input(\"ICNB_transit_switch\", node),\n             \"icnbrec_transit_switch_by_name\");\n-    input_data->icsbrec_port_binding_by_az =\n-        engine_ovsdb_node_get_index(\n-            engine_get_input(\"ICSB_port_binding\", node),\n-            \"icsbrec_port_binding_by_az\");\n-    input_data->icsbrec_route_by_az =\n-        engine_ovsdb_node_get_index(\n-            engine_get_input(\"ICSB_route\", node),\n-            \"icsbrec_route_by_az\");\n-    input_data->icsbrec_route_by_ts =\n-        engine_ovsdb_node_get_index(\n-            engine_get_input(\"ICSB_route\", node),\n-            \"icsbrec_route_by_ts\");\n-    input_data->icsbrec_route_by_ts_az =\n-        engine_ovsdb_node_get_index(\n-            engine_get_input(\"ICSB_route\", node),\n-            \"icsbrec_route_by_ts_az\");\n     input_data->icsbrec_service_monitor_by_source_az =\n         engine_ovsdb_node_get_index(\n             engine_get_input(\"ICSB_service_monitor\", node),\ndiff --git a/ic/en-route.c b/ic/en-route.c\nnew file mode 100644\nindex 000000000..f46e303e3\n--- /dev/null\n+++ b/ic/en-route.c\n@@ -0,0 +1,1554 @@\n+/*\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at:\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+*/\n+\n+#include <config.h>\n+\n+#include <getopt.h>\n+#include <stdlib.h>\n+#include <stdio.h>\n+#include \"vec.h\"\n+\n+/* OVS includes. */\n+#include \"openvswitch/vlog.h\"\n+\n+/* OVN includes. */\n+#include \"ovn-ic.h\"\n+#include \"en-route.h\"\n+#include \"inc-proc-ic.h\"\n+#include \"lib/inc-proc-eng.h\"\n+#include \"lib/ovn-nb-idl.h\"\n+#include \"lib/ovn-ic-nb-idl.h\"\n+#include \"lib/ovn-ic-sb-idl.h\"\n+#include \"lib/ovn-util.h\"\n+#include \"lib/stopwatch-names.h\"\n+#include \"coverage.h\"\n+#include \"stopwatch.h\"\n+#include \"stopwatch-names.h\"\n+\n+VLOG_DEFINE_THIS_MODULE(en_route);\n+COVERAGE_DEFINE(route_run);\n+\n+static void\n+route_run(const struct engine_context *eng_ctx,\n+          struct route_input *route_input,\n+          struct ed_type_route *route_data,\n+          const struct nbrec_logical_router_table *nb_lr_table,\n+          const struct nbrec_nb_global_table *nb_global_table);\n+static void route_init(struct ed_type_route *data);\n+static void route_destroy(struct ed_type_route *data);\n+static void route_clear(struct ed_type_route *data);\n+static void route_get_input_data(struct engine_node *node,\n+                                        struct route_input *input_data);\n+\n+static uint32_t\n+    ic_route_hash(const struct in6_addr *prefix, unsigned int plen,\n+                  const struct in6_addr *nexthop, const char *origin,\n+                  const char *route_table);\n+static struct ic_route_info *\n+    ic_route_find(struct hmap *routes, const struct in6_addr *prefix,\n+                  unsigned int plen, const struct in6_addr *nexthop,\n+                  const char *origin, const char *route_table, uint32_t hash);\n+static struct ic_router_info *\n+    ic_router_find(struct hmap *ic_lrs, const struct nbrec_logical_router *lr);\n+static bool\n+    parse_route(const char *s_prefix, const char *s_nexthop,\n+                struct in6_addr *prefix, unsigned int *plen,\n+                struct in6_addr *nexthop);\n+static bool\n+    add_to_routes_learned(struct hmap *routes_learned,\n+                      const struct nbrec_logical_router_static_route *nb_route,\n+                      const struct nbrec_logical_router *nb_lr);\n+static bool\n+    get_nexthop_from_lport_addresses(bool is_v4,\n+                                     const struct lport_addresses *laddr,\n+                                     struct in6_addr *nexthop);\n+static bool\n+    prefix_is_filtered(struct in6_addr *prefix,\n+                       unsigned int plen,\n+                       const struct nbrec_logical_router *nb_lr,\n+                       const struct nbrec_logical_router_port *ts_lrp,\n+                       bool is_advertisement);\n+static bool\n+    prefix_is_deny_filtered(struct in6_addr *prefix,\n+                            unsigned int plen,\n+                            const struct smap *nb_options,\n+                            const struct nbrec_logical_router *nb_lr,\n+                            const struct nbrec_logical_router_port *ts_lrp,\n+                            bool is_advertisement);\n+static bool\n+    route_need_advertise(const char *policy,\n+                         struct in6_addr *prefix,\n+                         unsigned int plen,\n+                         const struct smap *nb_options,\n+                         const struct nbrec_logical_router *nb_lr,\n+                         const struct nbrec_logical_router_port *ts_lrp);\n+static void\n+    add_to_routes_ad(struct hmap *routes_ad, const struct in6_addr prefix,\n+                     unsigned int plen, const struct in6_addr nexthop,\n+                     const char *origin, const char *route_table,\n+                     const struct nbrec_logical_router_port *nb_lrp,\n+                     const struct nbrec_logical_router_static_route *nb_route,\n+                     const struct nbrec_logical_router *nb_lr,\n+                     const struct nbrec_load_balancer *nb_lb,\n+                     const char *route_tag);\n+static void\n+    add_static_to_routes_ad(struct hmap *routes_ad,\n+        const struct nbrec_logical_router_static_route *nb_route,\n+        const struct nbrec_logical_router *nb_lr,\n+        const struct lport_addresses *nexthop_addresses,\n+        const struct smap *nb_options,\n+        const char *route_tag,\n+        const struct nbrec_logical_router_port *ts_lrp);\n+static void\n+    add_network_to_routes_ad(struct hmap *routes_ad, const char *network,\n+                            const struct nbrec_logical_router_port *nb_lrp,\n+                            const struct lport_addresses *nexthop_addresses,\n+                            const struct smap *nb_options,\n+                            const struct nbrec_logical_router *nb_lr,\n+                            const char *route_tag,\n+                            const struct nbrec_logical_router_port *ts_lrp);\n+static void\n+    add_lb_vip_to_routes_ad(struct hmap *routes_ad, const char *vip_key,\n+                            const struct nbrec_load_balancer *nb_lb,\n+                            const struct lport_addresses *nexthop_addresses,\n+                            const struct smap *nb_options,\n+                            const struct nbrec_logical_router *nb_lr,\n+                            const char *route_tag,\n+                            const struct nbrec_logical_router_port *ts_lrp);\n+static bool\n+    route_has_local_gw(const struct nbrec_logical_router *lr,\n+                       const char *route_table, const char *ip_prefix);\n+static bool\n+    lrp_has_neighbor_in_ts(const struct nbrec_logical_router_port *lrp,\n+                           struct in6_addr *nexthop);\n+static bool\n+    route_matches_local_lb(const struct nbrec_load_balancer *nb_lb,\n+                           const char *ip_prefix);\n+static bool\n+    route_need_learn(const struct nbrec_logical_router *lr,\n+                     const struct icsbrec_route *isb_route,\n+                     struct in6_addr *prefix, unsigned int plen,\n+                     const struct smap *nb_options,\n+                     const struct nbrec_logical_router_port *ts_lrp,\n+                     struct in6_addr *nexthop);\n+static const char *\n+    get_lrp_name_by_ts_port_name(struct route_input *ic,\n+                                 const char *ts_port_name);\n+static const struct nbrec_logical_router_port *\n+    find_lrp_of_nexthop(struct route_input *ic,\n+                        const struct icsbrec_route *isb_route);\n+static bool\n+    lrp_is_ts_port(struct route_input *ic, struct ic_router_info *ic_lr,\n+                   const char *lrp_name);\n+static void\n+    sync_learned_routes(const struct engine_context *ctx,\n+                        struct route_input *ic, struct ic_router_info *ic_lr,\n+                        const struct nbrec_nb_global_table *nb_global_table);\n+static void\n+    ad_route_sync_external_ids(const struct ic_route_info *route_adv,\n+                               const struct icsbrec_route *isb_route);\n+static void\n+    advertise_routes(const struct engine_context *ctx,\n+                     struct route_input *ic,\n+                     const struct icsbrec_availability_zone *az,\n+                     const char *ts_name, struct hmap *routes_ad);\n+static void\n+    build_ts_routes_to_adv(struct route_input *ic,\n+                           struct ic_router_info *ic_lr,\n+                           struct hmap *routes_ad,\n+                           struct lport_addresses *ts_port_addrs,\n+                           const struct nbrec_nb_global *nb_global,\n+                           const char *ts_route_table,\n+                           const char *route_tag,\n+                           const struct nbrec_logical_router_port *ts_lrp);\n+static void\n+    collect_lr_routes(struct route_input *ic,\n+                      struct ic_router_info *ic_lr,\n+                      struct shash *routes_ad_by_ts,\n+                      const struct nbrec_nb_global_table *nb_global_table);\n+static void\n+    delete_orphan_ic_routes(struct route_input *ic,\n+                            const struct icsbrec_availability_zone *az);\n+\n+static void\n+route_get_input_data(struct engine_node *node,\n+                     struct route_input *input_data)\n+{\n+    /* Indexes */\n+    input_data->nbrec_ls_by_name =\n+        engine_ovsdb_node_get_index(\n+            engine_get_input(\"NB_logical_switch\", node),\n+            \"nbrec_ls_by_name\");\n+    input_data->nbrec_port_by_name =\n+        engine_ovsdb_node_get_index(\n+            engine_get_input(\"NB_logical_switch\", node),\n+            \"nbrec_port_by_name\");\n+    input_data->nbrec_lrp_by_name =\n+        engine_ovsdb_node_get_index(\n+            engine_get_input(\"NB_logical_router\", node),\n+            \"nbrec_lrp_by_name\");\n+    input_data->icnbrec_transit_switch_by_name =\n+        engine_ovsdb_node_get_index(\n+            engine_get_input(\"ICNB_transit_switch\", node),\n+            \"icnbrec_transit_switch_by_name\");\n+    input_data->icsbrec_port_binding_by_az =\n+        engine_ovsdb_node_get_index(\n+            engine_get_input(\"ICSB_port_binding\", node),\n+            \"icsbrec_port_binding_by_az\");\n+    input_data->icsbrec_route_by_az =\n+        engine_ovsdb_node_get_index(\n+            engine_get_input(\"ICSB_route\", node),\n+            \"icsbrec_route_by_az\");\n+    input_data->icsbrec_route_by_ts =\n+        engine_ovsdb_node_get_index(\n+            engine_get_input(\"ICSB_route\", node),\n+            \"icsbrec_route_by_ts\");\n+    input_data->icsbrec_route_by_ts_az =\n+        engine_ovsdb_node_get_index(\n+            engine_get_input(\"ICSB_route\", node),\n+            \"icsbrec_route_by_ts_az\");\n+}\n+\n+enum engine_node_state\n+en_route_run(struct engine_node *node, void *data)\n+{\n+    const struct engine_context *eng_ctx = engine_get_context();\n+    struct ed_type_route *route_data = data;\n+    struct route_input route_input;\n+\n+    route_clear(route_data);\n+\n+    const struct nbrec_logical_router_table *nb_lr_table =\n+        EN_OVSDB_GET(engine_get_input(\"NB_logical_router\", node));\n+    const struct nbrec_nb_global_table *nb_global_table =\n+        EN_OVSDB_GET(engine_get_input(\"NB_nb_global\", node));\n+\n+    route_get_input_data(node, &route_input);\n+    route_input.runned_az = eng_ctx->client_ctx;\n+\n+    COVERAGE_INC(route_run);\n+    stopwatch_start(OVN_IC_ROUTE_RUN_STOPWATCH_NAME, time_usec());\n+    route_run(eng_ctx, &route_input, route_data, nb_lr_table, nb_global_table);\n+    stopwatch_stop(OVN_IC_ROUTE_RUN_STOPWATCH_NAME, time_usec());\n+\n+    return EN_UPDATED;\n+}\n+\n+void *\n+en_route_init(struct engine_node *node OVS_UNUSED,\n+              struct engine_arg *arg OVS_UNUSED)\n+{\n+    struct ed_type_route *data = xzalloc(sizeof *data);\n+    route_init(data);\n+    return data;\n+}\n+\n+void\n+en_route_cleanup(void *data)\n+{\n+    route_destroy(data);\n+}\n+\n+static void\n+route_init(struct ed_type_route *data)\n+{\n+    hmap_init(&data->pb_tnlids);\n+    shash_init(&data->switch_all_local_pbs);\n+    shash_init(&data->router_all_local_pbs);\n+}\n+\n+static void\n+route_destroy(struct ed_type_route *data)\n+{\n+    route_clear(data);\n+    ovn_destroy_tnlids(&data->pb_tnlids);\n+\n+    shash_destroy(&data->switch_all_local_pbs);\n+    shash_destroy(&data->router_all_local_pbs);\n+}\n+\n+static void\n+route_clear(struct ed_type_route *data)\n+{\n+    ovn_destroy_tnlids(&data->pb_tnlids);\n+    hmap_init(&data->pb_tnlids);\n+\n+    shash_clear(&data->switch_all_local_pbs);\n+    shash_clear(&data->router_all_local_pbs);\n+}\n+\n+static void\n+route_run(const struct engine_context *eng_ctx,\n+          struct route_input *route_input,\n+          struct ed_type_route *route_data OVS_UNUSED,\n+          const struct nbrec_logical_router_table *nb_lr_table,\n+          const struct nbrec_nb_global_table *nb_global_table)\n+{\n+    if (!eng_ctx->ovnisb_idl_txn || !eng_ctx->ovnnb_idl_txn) {\n+        return;\n+    }\n+\n+    delete_orphan_ic_routes(route_input, route_input->runned_az);\n+\n+    struct hmap ic_lrs = HMAP_INITIALIZER(&ic_lrs);\n+    const struct icsbrec_port_binding *isb_pb;\n+    const struct icsbrec_port_binding *isb_pb_key =\n+        icsbrec_port_binding_index_init_row(\n+            route_input->icsbrec_port_binding_by_az);\n+    icsbrec_port_binding_index_set_availability_zone(isb_pb_key,\n+        route_input->runned_az);\n+\n+    /* Each port on TS maps to a logical router, which is stored in the\n+     * external_ids:router-id of the IC SB port_binding record.\n+     * Here we build info for interconnected Logical Router:\n+     * collect IC Port Binding to process routes sync later on. */\n+    ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,\n+        route_input->icsbrec_port_binding_by_az)\n+    {\n+        if (ic_pb_get_type(isb_pb) == IC_ROUTER_PORT) {\n+            continue;\n+        }\n+        const struct nbrec_logical_switch_port *nb_lsp;\n+\n+        nb_lsp = get_lsp_by_ts_port_name(route_input->nbrec_port_by_name,\n+                                         isb_pb->logical_port);\n+        if (!strcmp(nb_lsp->type, \"switch\")) {\n+            VLOG_DBG(\"IC-SB Port_Binding '%s' on ts '%s' corresponds to a \"\n+                     \"switch port, not considering for route collection.\",\n+                     isb_pb->logical_port, isb_pb->transit_switch);\n+            continue;\n+        }\n+\n+        const char *ts_lrp_name =\n+            get_lrp_name_by_ts_port_name(route_input, isb_pb->logical_port);\n+        if (!ts_lrp_name) {\n+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+            VLOG_WARN_RL(&rl, \"Route sync ignores port %s on ts %s because \"\n+                         \"logical router port is not found in NB. Deleting it\",\n+                         isb_pb->logical_port, isb_pb->transit_switch);\n+            icsbrec_port_binding_delete(isb_pb);\n+            continue;\n+        }\n+\n+        struct uuid lr_uuid;\n+        if (!smap_get_uuid(&isb_pb->external_ids, \"router-id\", &lr_uuid)) {\n+            VLOG_DBG(\"IC-SB Port_Binding %s doesn't have \"\n+                     \"external_ids:router-id set.\", isb_pb->logical_port);\n+            continue;\n+        }\n+\n+        const struct nbrec_logical_router *lr\n+            = nbrec_logical_router_table_get_for_uuid(nb_lr_table, &lr_uuid);\n+        if (!lr) {\n+            continue;\n+        }\n+\n+        struct ic_router_info *ic_lr = ic_router_find(&ic_lrs, lr);\n+        if (!ic_lr) {\n+            ic_lr = xzalloc(sizeof *ic_lr);\n+            ic_lr->lr = lr;\n+            ic_lr->isb_pbs =\n+                VECTOR_EMPTY_INITIALIZER(const struct icsbrec_port_binding *);\n+            hmap_init(&ic_lr->routes_learned);\n+            hmap_insert(&ic_lrs, &ic_lr->node, uuid_hash(&lr->header_.uuid));\n+        }\n+        vector_push(&ic_lr->isb_pbs, &isb_pb);\n+    }\n+    icsbrec_port_binding_index_destroy_row(isb_pb_key);\n+\n+    struct ic_router_info *ic_lr;\n+    struct shash routes_ad_by_ts = SHASH_INITIALIZER(&routes_ad_by_ts);\n+    HMAP_FOR_EACH_SAFE (ic_lr, node, &ic_lrs) {\n+        collect_lr_routes(route_input, ic_lr, &routes_ad_by_ts,\n+                          nb_global_table);\n+        sync_learned_routes(eng_ctx, route_input, ic_lr, nb_global_table);\n+        vector_destroy(&ic_lr->isb_pbs);\n+        hmap_destroy(&ic_lr->routes_learned);\n+        hmap_remove(&ic_lrs, &ic_lr->node);\n+        free(ic_lr);\n+    }\n+    struct shash_node *node;\n+    SHASH_FOR_EACH (node, &routes_ad_by_ts) {\n+        advertise_routes(eng_ctx, route_input, route_input->runned_az,\n+                         node->name, node->data);\n+        hmap_destroy(node->data);\n+    }\n+    shash_destroy_free_data(&routes_ad_by_ts);\n+    hmap_destroy(&ic_lrs);\n+}\n+\n+static uint32_t\n+ic_route_hash(const struct in6_addr *prefix, unsigned int plen,\n+              const struct in6_addr *nexthop, const char *origin,\n+              const char *route_table)\n+{\n+    uint32_t basis = hash_bytes(prefix, sizeof *prefix, (uint32_t) plen);\n+    basis = hash_string(origin, basis);\n+    basis = hash_string(route_table, basis);\n+    return hash_bytes(nexthop, sizeof *nexthop, basis);\n+}\n+\n+static struct ic_route_info *\n+ic_route_find(struct hmap *routes, const struct in6_addr *prefix,\n+              unsigned int plen, const struct in6_addr *nexthop,\n+              const char *origin, const char *route_table, uint32_t hash)\n+{\n+    struct ic_route_info *r;\n+    if (!hash) {\n+        hash = ic_route_hash(prefix, plen, nexthop, origin, route_table);\n+    }\n+    HMAP_FOR_EACH_WITH_HASH (r, node, hash, routes) {\n+        if (ipv6_addr_equals(&r->prefix, prefix) &&\n+            r->plen == plen &&\n+            ipv6_addr_equals(&r->nexthop, nexthop) &&\n+            !strcmp(r->origin, origin) &&\n+            !strcmp(r->route_table ? r->route_table : \"\", route_table)) {\n+            return r;\n+        }\n+    }\n+    return NULL;\n+}\n+\n+static struct ic_router_info *\n+ic_router_find(struct hmap *ic_lrs, const struct nbrec_logical_router *lr)\n+{\n+    struct ic_router_info *ic_lr;\n+    HMAP_FOR_EACH_WITH_HASH (ic_lr, node, uuid_hash(&lr->header_.uuid),\n+                             ic_lrs) {\n+        if (ic_lr->lr == lr) {\n+           return ic_lr;\n+        }\n+    }\n+    return NULL;\n+}\n+\n+static bool\n+parse_route(const char *s_prefix, const char *s_nexthop,\n+            struct in6_addr *prefix, unsigned int *plen,\n+            struct in6_addr *nexthop)\n+{\n+    if (!ip46_parse_cidr(s_prefix, prefix, plen)) {\n+        return false;\n+    }\n+\n+    unsigned int nlen;\n+    if (strcmp(s_nexthop, \"discard\") &&\n+        !ip46_parse_cidr(s_nexthop, nexthop, &nlen)) {\n+        return false;\n+    }\n+\n+    /* Do not learn routes with link-local next hop. */\n+    return !in6_is_lla(nexthop);\n+}\n+\n+/* Return false if can't be added due to bad format. */\n+static bool\n+add_to_routes_learned(struct hmap *routes_learned,\n+                      const struct nbrec_logical_router_static_route *nb_route,\n+                      const struct nbrec_logical_router *nb_lr)\n+{\n+    struct in6_addr prefix, nexthop;\n+    unsigned int plen;\n+    if (!parse_route(nb_route->ip_prefix, nb_route->nexthop,\n+                     &prefix, &plen, &nexthop)) {\n+        return false;\n+    }\n+    const char *origin = smap_get_def(&nb_route->options, \"origin\", \"\");\n+    if (ic_route_find(routes_learned, &prefix, plen, &nexthop, origin,\n+                      nb_route->route_table, 0)) {\n+        /* Route was added to learned on previous iteration. */\n+        return true;\n+    }\n+\n+    struct ic_route_info *ic_route = xzalloc(sizeof *ic_route);\n+    ic_route->prefix = prefix;\n+    ic_route->plen = plen;\n+    ic_route->nexthop = nexthop;\n+    ic_route->nb_route = nb_route;\n+    ic_route->origin = origin;\n+    ic_route->route_table = nb_route->route_table;\n+    ic_route->nb_lr = nb_lr;\n+    hmap_insert(routes_learned, &ic_route->node,\n+                ic_route_hash(&prefix, plen, &nexthop, origin,\n+                              nb_route->route_table));\n+    return true;\n+}\n+\n+static bool\n+get_nexthop_from_lport_addresses(bool is_v4,\n+                                 const struct lport_addresses *laddr,\n+                                 struct in6_addr *nexthop)\n+{\n+    if (is_v4) {\n+        if (!laddr->n_ipv4_addrs) {\n+            return false;\n+        }\n+        in6_addr_set_mapped_ipv4(nexthop, laddr->ipv4_addrs[0].addr);\n+        return true;\n+    }\n+\n+    /* ipv6 */\n+    if (laddr->n_ipv6_addrs) {\n+        *nexthop = laddr->ipv6_addrs[0].addr;\n+        return true;\n+    }\n+\n+    /* ipv6 link local */\n+    in6_generate_lla(laddr->ea, nexthop);\n+    return true;\n+}\n+\n+static bool\n+prefix_is_filtered(struct in6_addr *prefix,\n+                   unsigned int plen,\n+                   const struct nbrec_logical_router *nb_lr,\n+                   const struct nbrec_logical_router_port *ts_lrp,\n+                   bool is_advertisement)\n+{\n+    struct ds filter_list = DS_EMPTY_INITIALIZER;\n+    const char *filter_direction = is_advertisement ? \"ic-route-filter-adv\" :\n+                                                      \"ic-route-filter-learn\";\n+    if (ts_lrp) {\n+        const char *lrp_route_filter = smap_get(&ts_lrp->options,\n+                                                filter_direction);\n+        if (lrp_route_filter) {\n+            ds_put_format(&filter_list, \"%s,\", lrp_route_filter);\n+        }\n+    }\n+    const char *lr_route_filter = smap_get(&nb_lr->options,\n+                                           filter_direction);\n+    if (lr_route_filter) {\n+        ds_put_format(&filter_list, \"%s,\", lr_route_filter);\n+    }\n+\n+    struct sset prefix_set = SSET_INITIALIZER(&prefix_set);\n+    sset_from_delimited_string(&prefix_set, ds_cstr(&filter_list), \",\");\n+\n+    bool matched = true;\n+    if (!sset_is_empty(&prefix_set)) {\n+        matched = find_prefix_in_set(prefix, plen, &prefix_set,\n+                                     filter_direction);\n+    }\n+\n+    ds_destroy(&filter_list);\n+    sset_destroy(&prefix_set);\n+    return matched;\n+}\n+\n+static bool\n+prefix_is_deny_filtered(struct in6_addr *prefix,\n+                        unsigned int plen,\n+                        const struct smap *nb_options,\n+                        const struct nbrec_logical_router *nb_lr,\n+                        const struct nbrec_logical_router_port *ts_lrp,\n+                        bool is_advertisement)\n+{\n+    struct ds deny_list = DS_EMPTY_INITIALIZER;\n+    const char *deny_key = is_advertisement ? \"ic-route-deny-adv\" :\n+                                              \"ic-route-deny-learn\";\n+\n+    if (ts_lrp) {\n+        const char *lrp_deny_filter = smap_get(&ts_lrp->options, deny_key);\n+        if (lrp_deny_filter) {\n+            ds_put_format(&deny_list, \"%s,\", lrp_deny_filter);\n+        }\n+    }\n+\n+    if (nb_lr) {\n+        const char *lr_deny_filter = smap_get(&nb_lr->options, deny_key);\n+        if (lr_deny_filter) {\n+            ds_put_format(&deny_list, \"%s,\", lr_deny_filter);\n+        }\n+    }\n+\n+    if (nb_options) {\n+        const char *global_deny = smap_get(nb_options, \"ic-route-denylist\");\n+        if (!global_deny || !global_deny[0]) {\n+            global_deny = smap_get(nb_options, \"ic-route-blacklist\");\n+        }\n+        if (global_deny && global_deny[0]) {\n+            ds_put_format(&deny_list, \"%s,\", global_deny);\n+        }\n+    }\n+\n+    struct sset prefix_set = SSET_INITIALIZER(&prefix_set);\n+    sset_from_delimited_string(&prefix_set, ds_cstr(&deny_list), \",\");\n+\n+    bool denied = false;\n+    if (!sset_is_empty(&prefix_set)) {\n+        denied = find_prefix_in_set(prefix, plen, &prefix_set, deny_key);\n+    }\n+\n+    ds_destroy(&deny_list);\n+    sset_destroy(&prefix_set);\n+    return denied;\n+}\n+\n+static bool\n+route_need_advertise(const char *policy,\n+                     struct in6_addr *prefix,\n+                     unsigned int plen,\n+                     const struct smap *nb_options,\n+                     const struct nbrec_logical_router *nb_lr,\n+                     const struct nbrec_logical_router_port *ts_lrp)\n+{\n+    if (!smap_get_bool(nb_options, \"ic-route-adv\", false)) {\n+        return false;\n+    }\n+\n+    if (plen == 0 &&\n+        !smap_get_bool(nb_options, \"ic-route-adv-default\", false)) {\n+        return false;\n+    }\n+\n+    if (policy && !strcmp(policy, \"src-ip\")) {\n+        return false;\n+    }\n+\n+    if (prefix_is_link_local(prefix, plen)) {\n+        return false;\n+    }\n+\n+    if (prefix_is_deny_filtered(prefix, plen, nb_options,\n+                                nb_lr, ts_lrp, true)) {\n+        return false;\n+    }\n+\n+    if (!prefix_is_filtered(prefix, plen, nb_lr, ts_lrp, true)) {\n+        return false;\n+    }\n+\n+    return true;\n+}\n+\n+static void\n+add_to_routes_ad(struct hmap *routes_ad, const struct in6_addr prefix,\n+                 unsigned int plen, const struct in6_addr nexthop,\n+                 const char *origin, const char *route_table,\n+                 const struct nbrec_logical_router_port *nb_lrp,\n+                 const struct nbrec_logical_router_static_route *nb_route,\n+                 const struct nbrec_logical_router *nb_lr,\n+                 const struct nbrec_load_balancer *nb_lb,\n+                 const char *route_tag)\n+{\n+    ovs_assert(nb_route || nb_lrp || nb_lb);\n+\n+    if (route_table == NULL) {\n+        route_table = \"\";\n+    }\n+\n+    uint hash = ic_route_hash(&prefix, plen, &nexthop, origin, route_table);\n+\n+    if (!ic_route_find(routes_ad, &prefix, plen, &nexthop, origin,\n+                       route_table, hash)) {\n+        struct ic_route_info *ic_route = xzalloc(sizeof *ic_route);\n+        ic_route->prefix = prefix;\n+        ic_route->plen = plen;\n+        ic_route->nexthop = nexthop;\n+        ic_route->nb_route = nb_route;\n+        ic_route->origin = origin;\n+        ic_route->route_table = route_table;\n+        ic_route->nb_lrp = nb_lrp;\n+        ic_route->nb_lr = nb_lr;\n+        ic_route->nb_lb = nb_lb;\n+        ic_route->route_tag = route_tag;\n+        hmap_insert(routes_ad, &ic_route->node, hash);\n+    } else {\n+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+        const char *msg_fmt = \"Duplicate %s route advertisement was \"\n+                              \"suppressed! NB %s uuid: \"UUID_FMT;\n+        if (nb_route) {\n+            VLOG_WARN_RL(&rl, msg_fmt, origin, \"route\",\n+                         UUID_ARGS(&nb_route->header_.uuid));\n+        } else if (nb_lb) {\n+            VLOG_WARN_RL(&rl, msg_fmt, origin, \"loadbalancer\",\n+                         UUID_ARGS(&nb_lb->header_.uuid));\n+        } else {\n+            VLOG_WARN_RL(&rl, msg_fmt, origin, \"lrp\",\n+                         UUID_ARGS(&nb_lrp->header_.uuid));\n+        }\n+    }\n+}\n+\n+static void\n+add_static_to_routes_ad(\n+    struct hmap *routes_ad,\n+    const struct nbrec_logical_router_static_route *nb_route,\n+    const struct nbrec_logical_router *nb_lr,\n+    const struct lport_addresses *nexthop_addresses,\n+    const struct smap *nb_options,\n+    const char *route_tag,\n+    const struct nbrec_logical_router_port *ts_lrp)\n+{\n+    struct in6_addr prefix, nexthop;\n+    unsigned int plen;\n+    if (!parse_route(nb_route->ip_prefix, nb_route->nexthop,\n+                     &prefix, &plen, &nexthop)) {\n+        return;\n+    }\n+\n+    if (!route_need_advertise(nb_route->policy, &prefix, plen, nb_options,\n+                              nb_lr, ts_lrp)) {\n+        return;\n+    }\n+\n+    if (!get_nexthop_from_lport_addresses(IN6_IS_ADDR_V4MAPPED(&prefix),\n+                                          nexthop_addresses,\n+                                          &nexthop)) {\n+        return;\n+    }\n+\n+    if (VLOG_IS_DBG_ENABLED()) {\n+        struct ds msg = DS_EMPTY_INITIALIZER;\n+\n+        ds_put_format(&msg, \"Advertising static route: %s -> %s, ic nexthop: \",\n+                      nb_route->ip_prefix, nb_route->nexthop);\n+\n+        if (IN6_IS_ADDR_V4MAPPED(&nexthop)) {\n+            ds_put_format(&msg, IP_FMT,\n+                          IP_ARGS(in6_addr_get_mapped_ipv4(&nexthop)));\n+        } else {\n+            ipv6_format_addr(&nexthop, &msg);\n+        }\n+\n+        ds_put_format(&msg, \", route_table: %s\", nb_route->route_table[0]\n+                                                 ? nb_route->route_table\n+                                                 : \"<main>\");\n+\n+        VLOG_DBG(\"%s\", ds_cstr(&msg));\n+        ds_destroy(&msg);\n+    }\n+\n+    add_to_routes_ad(routes_ad, prefix, plen, nexthop, ROUTE_ORIGIN_STATIC,\n+                     nb_route->route_table, NULL, nb_route, nb_lr,\n+                     NULL, route_tag);\n+}\n+\n+static void\n+add_network_to_routes_ad(struct hmap *routes_ad, const char *network,\n+                         const struct nbrec_logical_router_port *nb_lrp,\n+                         const struct lport_addresses *nexthop_addresses,\n+                         const struct smap *nb_options,\n+                         const struct nbrec_logical_router *nb_lr,\n+                         const char *route_tag,\n+                         const struct nbrec_logical_router_port *ts_lrp)\n+{\n+    struct in6_addr prefix, nexthop;\n+    unsigned int plen;\n+    if (!ip46_parse_cidr(network, &prefix, &plen)) {\n+        return;\n+    }\n+\n+    if (!route_need_advertise(NULL, &prefix, plen, nb_options,\n+                              nb_lr, ts_lrp)) {\n+        VLOG_DBG(\"Route ad: skip network %s of lrp %s.\",\n+                 network, nb_lrp->name);\n+        return;\n+    }\n+\n+    if (!get_nexthop_from_lport_addresses(IN6_IS_ADDR_V4MAPPED(&prefix),\n+                                          nexthop_addresses,\n+                                          &nexthop)) {\n+        return;\n+    }\n+\n+    if (VLOG_IS_DBG_ENABLED()) {\n+        struct ds msg = DS_EMPTY_INITIALIZER;\n+\n+        ds_put_format(&msg, \"Adding direct network route to <main> routing \"\n+                      \"table: %s of lrp %s, nexthop \", network, nb_lrp->name);\n+\n+        if (IN6_IS_ADDR_V4MAPPED(&nexthop)) {\n+            ds_put_format(&msg, IP_FMT,\n+                          IP_ARGS(in6_addr_get_mapped_ipv4(&nexthop)));\n+        } else {\n+            ipv6_format_addr(&nexthop, &msg);\n+        }\n+\n+        VLOG_DBG(\"%s\", ds_cstr(&msg));\n+        ds_destroy(&msg);\n+    }\n+\n+    /* directly-connected routes go to <main> route table */\n+    add_to_routes_ad(routes_ad, prefix, plen, nexthop, ROUTE_ORIGIN_CONNECTED,\n+                     NULL, nb_lrp, NULL, nb_lr, NULL, route_tag);\n+}\n+\n+static void\n+add_lb_vip_to_routes_ad(struct hmap *routes_ad, const char *vip_key,\n+                        const struct nbrec_load_balancer *nb_lb,\n+                        const struct lport_addresses *nexthop_addresses,\n+                        const struct smap *nb_options,\n+                        const struct nbrec_logical_router *nb_lr,\n+                        const char *route_tag,\n+                        const struct nbrec_logical_router_port *ts_lrp)\n+{\n+    char *vip_str = NULL;\n+    struct in6_addr vip_ip, nexthop;\n+    uint16_t vip_port;\n+    int addr_family;\n+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+\n+    if (!ip_address_and_port_from_lb_key(vip_key, &vip_str, &vip_ip,\n+                                         &vip_port, &addr_family)) {\n+        VLOG_WARN_RL(&rl, \"Route ad: Parsing failed for lb vip %s\", vip_key);\n+        return;\n+    }\n+    if (vip_str == NULL) {\n+        return;\n+    }\n+    unsigned int plen = (addr_family == AF_INET) ? 32 : 128;\n+    if (!route_need_advertise(NULL, &vip_ip, plen, nb_options,\n+                              nb_lr, ts_lrp)) {\n+        VLOG_DBG(\"Route ad: skip lb vip %s.\", vip_key);\n+        goto out;\n+    }\n+    if (!get_nexthop_from_lport_addresses(IN6_IS_ADDR_V4MAPPED(&vip_ip),\n+                                          nexthop_addresses,\n+                                          &nexthop)) {\n+        VLOG_WARN_RL(&rl, \"Route ad: failed to get nexthop for lb vip\");\n+        goto out;\n+    }\n+\n+    if (VLOG_IS_DBG_ENABLED()) {\n+        struct ds msg = DS_EMPTY_INITIALIZER;\n+\n+        ds_put_format(&msg, \"Adding lb vip route to <main> routing \"\n+                      \"table: %s, nexthop \", vip_str);\n+\n+        if (IN6_IS_ADDR_V4MAPPED(&nexthop)) {\n+            ds_put_format(&msg, IP_FMT,\n+                          IP_ARGS(in6_addr_get_mapped_ipv4(&nexthop)));\n+        } else {\n+            ipv6_format_addr(&nexthop, &msg);\n+        }\n+\n+        VLOG_DBG(\"%s\", ds_cstr(&msg));\n+        ds_destroy(&msg);\n+    }\n+\n+    /* Lb vip routes go to <main> route table */\n+    add_to_routes_ad(routes_ad, vip_ip, plen, nexthop, ROUTE_ORIGIN_LB,\n+                     NULL, NULL, NULL, nb_lr, nb_lb, route_tag);\n+out:\n+    free(vip_str);\n+}\n+\n+static bool\n+route_has_local_gw(const struct nbrec_logical_router *lr,\n+                   const char *route_table, const char *ip_prefix) {\n+\n+    const struct nbrec_logical_router_static_route *route;\n+    for (int i = 0; i < lr->n_static_routes; i++) {\n+        route = lr->static_routes[i];\n+        if (!smap_get(&route->external_ids, \"ic-learned-route\") &&\n+            !strcmp(route->route_table, route_table) &&\n+            !strcmp(route->ip_prefix, ip_prefix)) {\n+            return true;\n+        }\n+    }\n+    return false;\n+}\n+\n+static bool\n+lrp_has_neighbor_in_ts(const struct nbrec_logical_router_port *lrp,\n+                       struct in6_addr *nexthop)\n+{\n+    if (!lrp || !nexthop) {\n+        return false;\n+    }\n+\n+    struct lport_addresses lrp_networks;\n+    if (!extract_lrp_networks(lrp, &lrp_networks)) {\n+        destroy_lport_addresses(&lrp_networks);\n+        return false;\n+    }\n+\n+    if (IN6_IS_ADDR_V4MAPPED(nexthop)) {\n+        ovs_be32 neigh_prefix_v4 = in6_addr_get_mapped_ipv4(nexthop);\n+        for (size_t i = 0; i < lrp_networks.n_ipv4_addrs; i++) {\n+            struct ipv4_netaddr address = lrp_networks.ipv4_addrs[i];\n+            if (address.network == (neigh_prefix_v4 & address.mask)) {\n+                destroy_lport_addresses(&lrp_networks);\n+                return true;\n+            }\n+        }\n+    } else {\n+        for (size_t i = 0; i < lrp_networks.n_ipv6_addrs; i++) {\n+            struct ipv6_netaddr address = lrp_networks.ipv6_addrs[i];\n+            struct in6_addr neigh_prefix = ipv6_addr_bitand(nexthop,\n+                                                            &address.mask);\n+            if (ipv6_addr_equals(&address.network, &neigh_prefix)) {\n+                destroy_lport_addresses(&lrp_networks);\n+                return true;\n+            }\n+        }\n+    }\n+\n+    destroy_lport_addresses(&lrp_networks);\n+    return false;\n+}\n+\n+static bool\n+route_matches_local_lb(const struct nbrec_load_balancer *nb_lb,\n+                       const char *ip_prefix)\n+{\n+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+    struct in6_addr prefix;\n+    unsigned int plen;\n+\n+    if (!ip46_parse_cidr(ip_prefix, &prefix, &plen)) {\n+        return false;\n+    }\n+\n+    struct smap_node *node;\n+    SMAP_FOR_EACH (node, &nb_lb->vips) {\n+        char *vip_str = NULL;\n+        struct in6_addr vip_ip;\n+        uint16_t vip_port;\n+        int addr_family;\n+        if (ip_address_and_port_from_lb_key(node->key, &vip_str,\n+                                            &vip_ip, &vip_port,\n+                                            &addr_family)) {\n+            if (IN6_IS_ADDR_V4MAPPED(&prefix) && addr_family == AF_INET) {\n+                ovs_be32 vip = in6_addr_get_mapped_ipv4(&vip_ip);\n+                ovs_be32 mask = be32_prefix_mask(plen);\n+\n+                if ((vip & mask) == in6_addr_get_mapped_ipv4(&prefix)) {\n+                    free(vip_str);\n+                    return true;\n+                }\n+            } else if (!IN6_IS_ADDR_V4MAPPED(&prefix)\n+                       && addr_family == AF_INET6) {\n+                struct in6_addr mask = ipv6_create_mask(plen);\n+                struct in6_addr vip_prefix = ipv6_addr_bitand(&vip_ip, &mask);\n+                if (ipv6_addr_equals(&prefix, &vip_prefix)) {\n+                    free(vip_str);\n+                    return true;\n+                }\n+            }\n+            free(vip_str);\n+        } else {\n+            VLOG_WARN_RL(&rl,\n+                         \"Route learn: Parsing failed for local lb vip %s\",\n+                         node->key);\n+        }\n+    }\n+    return false;\n+}\n+\n+static bool\n+route_need_learn(const struct nbrec_logical_router *lr,\n+                 const struct icsbrec_route *isb_route,\n+                 struct in6_addr *prefix, unsigned int plen,\n+                 const struct smap *nb_options,\n+                 const struct nbrec_logical_router_port *ts_lrp,\n+                 struct in6_addr *nexthop)\n+{\n+    if (!smap_get_bool(nb_options, \"ic-route-learn\", false)) {\n+        return false;\n+    }\n+\n+    if (plen == 0 &&\n+        !smap_get_bool(nb_options, \"ic-route-learn-default\", false)) {\n+        return false;\n+    }\n+\n+    if (!strcmp(isb_route->origin, ROUTE_ORIGIN_LB) &&\n+        !smap_get_bool(nb_options, \"ic-route-learn-lb\", false)) {\n+        return false;\n+    }\n+\n+    if (!lrouter_is_enabled(lr)) {\n+        return false;\n+    }\n+\n+    if (prefix_is_link_local(prefix, plen)) {\n+        return false;\n+    }\n+\n+    if (prefix_is_deny_filtered(prefix, plen, nb_options, lr, ts_lrp, false)) {\n+        return false;\n+    }\n+\n+    if (!prefix_is_filtered(prefix, plen, lr, ts_lrp, false)) {\n+        return false;\n+    }\n+\n+    if (route_has_local_gw(lr, isb_route->route_table, isb_route->ip_prefix)) {\n+        VLOG_DBG(\"Skip learning %s (rtb:%s) route, as we've got one with \"\n+                 \"local GW\", isb_route->ip_prefix, isb_route->route_table);\n+        return false;\n+    }\n+\n+    if (!lrp_has_neighbor_in_ts(ts_lrp, nexthop)) {\n+        return false;\n+    }\n+\n+    for (size_t i = 0; i < lr->n_load_balancer; i++) {\n+        if (route_matches_local_lb(lr->load_balancer[i],\n+                                   isb_route->ip_prefix)) {\n+            VLOG_DBG(\"Skip learning %s (rtb:%s) route, as we've got local\"\n+                     \" LB with matching VIP\", isb_route->ip_prefix,\n+                     isb_route->route_table);\n+            return false;\n+        }\n+    }\n+    for (size_t i = 0; i < lr->n_load_balancer_group; i++) {\n+        const struct nbrec_load_balancer_group *nb_lbg =\n+            lr->load_balancer_group[i];\n+        for (size_t j = 0; j < nb_lbg->n_load_balancer; j++) {\n+            if (route_matches_local_lb(nb_lbg->load_balancer[j],\n+                                       isb_route->ip_prefix)) {\n+                VLOG_DBG(\"Skip learning %s (rtb:%s) route, as we've got local\"\n+                         \" LB with matching VIP\", isb_route->ip_prefix,\n+                         isb_route->route_table);\n+                return false;\n+            }\n+        }\n+    }\n+\n+    return true;\n+}\n+\n+static const char *\n+get_lrp_name_by_ts_port_name(struct route_input *ic, const char *ts_port_name)\n+{\n+    const struct nbrec_logical_switch_port *nb_lsp;\n+\n+    nb_lsp = get_lsp_by_ts_port_name(ic->nbrec_port_by_name, ts_port_name);\n+    if (!nb_lsp) {\n+        return NULL;\n+    }\n+\n+    return smap_get(&nb_lsp->options, \"router-port\");\n+}\n+\n+static const struct nbrec_logical_router_port *\n+find_lrp_of_nexthop(struct route_input *ic,\n+                    const struct icsbrec_route *isb_route)\n+{\n+    const struct nbrec_logical_router_port *lrp;\n+    const struct nbrec_logical_switch *ls;\n+    ls = find_ts_in_nb(ic->nbrec_ls_by_name, isb_route->transit_switch);\n+    if (!ls) {\n+        return NULL;\n+    }\n+\n+    struct in6_addr nexthop;\n+    if (!ip46_parse(isb_route->nexthop, &nexthop)) {\n+        return NULL;\n+    }\n+\n+    for (size_t i = 0; i < ls->n_ports; i++) {\n+        char *lsp_name = ls->ports[i]->name;\n+        const char *lrp_name = get_lrp_name_by_ts_port_name(ic,\n+                                                            lsp_name);\n+        if (!lrp_name) {\n+            continue;\n+        }\n+\n+        lrp = get_lrp_by_lrp_name(ic->nbrec_lrp_by_name, lrp_name);\n+        if (!lrp) {\n+            continue;\n+        }\n+\n+        struct lport_addresses lrp_networks;\n+        if (!extract_lrp_networks(lrp, &lrp_networks)) {\n+            destroy_lport_addresses(&lrp_networks);\n+            continue;\n+        }\n+\n+        if (IN6_IS_ADDR_V4MAPPED(&nexthop)) {\n+            ovs_be32 nexthop_v4 = in6_addr_get_mapped_ipv4(&nexthop);\n+            for (size_t i_v4 = 0; i_v4  < lrp_networks.n_ipv4_addrs; i_v4++) {\n+                struct ipv4_netaddr address = lrp_networks.ipv4_addrs[i_v4];\n+                if (address.addr == nexthop_v4) {\n+                    destroy_lport_addresses(&lrp_networks);\n+                    return lrp;\n+                }\n+            }\n+        } else {\n+            for (size_t i_v6 = 0; i_v6 < lrp_networks.n_ipv6_addrs; i_v6++) {\n+                struct ipv6_netaddr address = lrp_networks.ipv6_addrs[i_v6];\n+                struct in6_addr nexthop_v6 = ipv6_addr_bitand(&nexthop,\n+                                                              &address.mask);\n+                if (ipv6_addr_equals(&address.network, &nexthop_v6)) {\n+                    destroy_lport_addresses(&lrp_networks);\n+                    return lrp;\n+                }\n+            }\n+        }\n+        destroy_lport_addresses(&lrp_networks);\n+    }\n+\n+    return NULL;\n+}\n+\n+static bool\n+lrp_is_ts_port(struct route_input *ic, struct ic_router_info *ic_lr,\n+               const char *lrp_name)\n+{\n+    const struct icsbrec_port_binding *isb_pb;\n+    const char *ts_lrp_name;\n+    VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) {\n+        ts_lrp_name = get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port);\n+        if (!strcmp(ts_lrp_name, lrp_name)) {\n+            return true;\n+        }\n+    }\n+    return false;\n+}\n+\n+static void\n+sync_learned_routes(const struct engine_context *ctx,\n+                    struct route_input *ic,\n+                    struct ic_router_info *ic_lr,\n+                    const struct nbrec_nb_global_table *nb_global_table)\n+{\n+    ovs_assert(ctx->ovnnb_idl_txn);\n+    const struct icsbrec_route *isb_route, *isb_route_key;\n+\n+    const struct nbrec_nb_global *nb_global =\n+        nbrec_nb_global_table_first(nb_global_table);\n+    ovs_assert(nb_global);\n+\n+    const char *lrp_name, *ts_route_table, *route_filter_tag;\n+    const struct icsbrec_port_binding *isb_pb;\n+    const struct nbrec_logical_router_port *lrp;\n+    VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) {\n+        if (!strcmp(isb_pb->address, \"\")) {\n+            continue;\n+        }\n+        lrp_name = get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port);\n+        lrp = get_lrp_by_lrp_name(ic->nbrec_lrp_by_name, lrp_name);\n+        if (lrp) {\n+            ts_route_table = smap_get_def(&lrp->options, \"route_table\", \"\");\n+            route_filter_tag = smap_get_def(&lrp->options,\n+                                            \"ic-route-filter-tag\", \"\");\n+        } else {\n+            ts_route_table = \"\";\n+            route_filter_tag = \"\";\n+        }\n+\n+        isb_route_key = icsbrec_route_index_init_row(ic->icsbrec_route_by_ts);\n+        icsbrec_route_index_set_transit_switch(isb_route_key,\n+                                               isb_pb->transit_switch);\n+\n+        ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key,\n+                                      ic->icsbrec_route_by_ts) {\n+            /* Filters ICSB routes, skipping those that either belong to\n+             * current logical router or are legacy routes from the current\n+             * availability zone (withoud lr-id).\n+             */\n+            const char *lr_id = smap_get(&isb_route->external_ids, \"lr-id\");\n+            struct uuid lr_uuid;\n+            if (lr_id) {\n+                if (!uuid_from_string(&lr_uuid, lr_id)\n+                    || uuid_equals(&ic_lr->lr->header_.uuid, &lr_uuid)) {\n+                    continue;\n+                }\n+            } else if (isb_route->availability_zone == ic->runned_az) {\n+                continue;\n+            }\n+\n+            const char *isb_route_tag = smap_get(&isb_route->external_ids,\n+                                                 \"ic-route-tag\");\n+            if (isb_route_tag  && !strcmp(isb_route_tag, route_filter_tag)) {\n+                VLOG_DBG(\"Skip learning route %s -> %s as its route tag \"\n+                         \"[%s] is filtered by the filter tag [%s] of TS LRP \",\n+                         isb_route->ip_prefix, isb_route->nexthop,\n+                         isb_route_tag, route_filter_tag);\n+                continue;\n+            }\n+\n+            if (isb_route->route_table[0] &&\n+                strcmp(isb_route->route_table, ts_route_table)) {\n+                if (VLOG_IS_DBG_ENABLED()) {\n+                    VLOG_DBG(\"Skip learning static route %s -> %s as either \"\n+                             \"its route table %s != %s of TS port or \",\n+                             isb_route->ip_prefix, isb_route->nexthop,\n+                             isb_route->route_table, ts_route_table);\n+                }\n+                continue;\n+            }\n+\n+            struct in6_addr prefix, nexthop;\n+            unsigned int plen;\n+            if (!parse_route(isb_route->ip_prefix, isb_route->nexthop,\n+                             &prefix, &plen, &nexthop)) {\n+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+                VLOG_WARN_RL(&rl, \"Bad route format in IC-SB: %s -> %s. \"\n+                             \"Ignored.\", isb_route->ip_prefix,\n+                             isb_route->nexthop);\n+                continue;\n+            }\n+            if (!route_need_learn(ic_lr->lr, isb_route, &prefix, plen,\n+                                  &nb_global->options, lrp, &nexthop)) {\n+                continue;\n+            }\n+\n+            struct ic_route_info *route_learned\n+                = ic_route_find(&ic_lr->routes_learned, &prefix, plen,\n+                                &nexthop, isb_route->origin,\n+                                isb_route->route_table, 0);\n+            if (route_learned) {\n+                /* Sync external-ids */\n+                struct uuid ext_id;\n+                smap_get_uuid(&route_learned->nb_route->external_ids,\n+                              \"ic-learned-route\", &ext_id);\n+                if (!uuid_equals(&ext_id, &isb_route->header_.uuid)) {\n+                    char *uuid_s =\n+                        xasprintf(UUID_FMT,\n+                                  UUID_ARGS(&isb_route->header_.uuid));\n+                    nbrec_logical_router_static_route_update_external_ids_setkey(\n+                        route_learned->nb_route, \"ic-learned-route\", uuid_s);\n+                    free(uuid_s);\n+                }\n+                hmap_remove(&ic_lr->routes_learned, &route_learned->node);\n+                free(route_learned);\n+            } else {\n+                /* Create the missing route in NB. */\n+                const struct nbrec_logical_router_static_route *nb_route =\n+                    nbrec_logical_router_static_route_insert(\n+                        ctx->ovnnb_idl_txn);\n+                nbrec_logical_router_static_route_set_ip_prefix(nb_route,\n+                    isb_route->ip_prefix);\n+                nbrec_logical_router_static_route_set_nexthop(nb_route,\n+                    isb_route->nexthop);\n+                char *uuid_s = xasprintf(UUID_FMT,\n+                                         UUID_ARGS(&isb_route->header_.uuid));\n+                nbrec_logical_router_static_route_set_route_table(nb_route,\n+                    isb_route->route_table);\n+                nbrec_logical_router_static_route_update_external_ids_setkey(\n+                    nb_route, \"ic-learned-route\", uuid_s);\n+                nbrec_logical_router_static_route_update_options_setkey(\n+                    nb_route, \"origin\", isb_route->origin);\n+                free(uuid_s);\n+                nbrec_logical_router_update_static_routes_addvalue(ic_lr->lr,\n+                    nb_route);\n+            }\n+        }\n+        icsbrec_route_index_destroy_row(isb_route_key);\n+    }\n+\n+    /* Delete extra learned routes. */\n+    struct ic_route_info *route_learned;\n+    HMAP_FOR_EACH_SAFE (route_learned, node, &ic_lr->routes_learned) {\n+        VLOG_DBG(\"Delete route %s -> %s that is not in IC-SB from NB.\",\n+                 route_learned->nb_route->ip_prefix,\n+                 route_learned->nb_route->nexthop);\n+        nbrec_logical_router_update_static_routes_delvalue(\n+            ic_lr->lr, route_learned->nb_route);\n+        hmap_remove(&ic_lr->routes_learned, &route_learned->node);\n+        free(route_learned);\n+    }\n+}\n+\n+static void\n+ad_route_sync_external_ids(const struct ic_route_info *route_adv,\n+                           const struct icsbrec_route *isb_route)\n+{\n+    struct uuid isb_ext_id, nb_id, isb_ext_lr_id, lr_id;\n+    const char *route_tag;\n+    smap_get_uuid(&isb_route->external_ids, \"nb-id\", &isb_ext_id);\n+    smap_get_uuid(&isb_route->external_ids, \"lr-id\", &isb_ext_lr_id);\n+    nb_id = route_adv->nb_lb ? route_adv->nb_lb->header_.uuid :\n+            route_adv->nb_route ? route_adv->nb_route->header_.uuid :\n+            route_adv->nb_lrp->header_.uuid;\n+\n+    lr_id = route_adv->nb_lr->header_.uuid;\n+    if (!uuid_equals(&isb_ext_id, &nb_id)) {\n+        char *uuid_s = xasprintf(UUID_FMT, UUID_ARGS(&nb_id));\n+        icsbrec_route_update_external_ids_setkey(isb_route, \"nb-id\",\n+                                                 uuid_s);\n+        free(uuid_s);\n+    }\n+    if (!uuid_equals(&isb_ext_lr_id, &lr_id)) {\n+        char *uuid_s = xasprintf(UUID_FMT, UUID_ARGS(&lr_id));\n+        icsbrec_route_update_external_ids_setkey(isb_route, \"lr-id\",\n+                                                 uuid_s);\n+        free(uuid_s);\n+    }\n+    if (strcmp(route_adv->route_tag, \"\")) {\n+        icsbrec_route_update_external_ids_setkey(isb_route, \"ic-route-tag\",\n+                                                 route_adv->route_tag);\n+    } else {\n+        route_tag = smap_get(&isb_route->external_ids, \"ic-route-tag\");\n+        if (route_tag) {\n+            icsbrec_route_update_external_ids_delkey(isb_route,\n+                                                     \"ic-route-tag\");\n+        }\n+    }\n+}\n+\n+/* Sync routes from routes_ad to IC-SB. */\n+static void\n+advertise_routes(const struct engine_context *ctx,\n+                 struct route_input *ic,\n+                 const struct icsbrec_availability_zone *az,\n+                 const char *ts_name, struct hmap *routes_ad)\n+{\n+    ovs_assert(ctx->ovnisb_idl_txn);\n+    const struct icsbrec_route *isb_route;\n+    const struct icsbrec_route *isb_route_key =\n+        icsbrec_route_index_init_row(ic->icsbrec_route_by_ts_az);\n+    icsbrec_route_index_set_transit_switch(isb_route_key, ts_name);\n+    icsbrec_route_index_set_availability_zone(isb_route_key, az);\n+\n+    ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key,\n+                                  ic->icsbrec_route_by_ts_az) {\n+        struct in6_addr prefix, nexthop;\n+        unsigned int plen;\n+\n+        if (!parse_route(isb_route->ip_prefix, isb_route->nexthop,\n+                         &prefix, &plen, &nexthop)) {\n+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+            VLOG_WARN_RL(&rl, \"Bad route format in IC-SB: %s -> %s. \"\n+                         \"Delete it.\",\n+                         isb_route->ip_prefix, isb_route->nexthop);\n+            icsbrec_route_delete(isb_route);\n+            continue;\n+        }\n+        struct ic_route_info *route_adv =\n+            ic_route_find(routes_ad, &prefix, plen, &nexthop,\n+                          isb_route->origin, isb_route->route_table, 0);\n+        if (!route_adv) {\n+            /* Delete the extra route from IC-SB. */\n+            VLOG_DBG(\"Delete route %s -> %s from IC-SB, which is not found\"\n+                     \" in local routes to be advertised.\",\n+                     isb_route->ip_prefix, isb_route->nexthop);\n+            icsbrec_route_delete(isb_route);\n+        } else {\n+            ad_route_sync_external_ids(route_adv, isb_route);\n+\n+            hmap_remove(routes_ad, &route_adv->node);\n+            free(route_adv);\n+        }\n+    }\n+    icsbrec_route_index_destroy_row(isb_route_key);\n+\n+    /* Create the missing routes in IC-SB */\n+    struct ic_route_info *route_adv;\n+    HMAP_FOR_EACH_SAFE (route_adv, node, routes_ad) {\n+        isb_route = icsbrec_route_insert(ctx->ovnisb_idl_txn);\n+        icsbrec_route_set_transit_switch(isb_route, ts_name);\n+        icsbrec_route_set_availability_zone(isb_route, az);\n+\n+        char *prefix_s, *nexthop_s;\n+        if (IN6_IS_ADDR_V4MAPPED(&route_adv->prefix)) {\n+            ovs_be32 ipv4 = in6_addr_get_mapped_ipv4(&route_adv->prefix);\n+            ovs_be32 nh = in6_addr_get_mapped_ipv4(&route_adv->nexthop);\n+            prefix_s = xasprintf(IP_FMT \"/%d\", IP_ARGS(ipv4), route_adv->plen);\n+            nexthop_s = xasprintf(IP_FMT, IP_ARGS(nh));\n+        } else {\n+            char network_s[INET6_ADDRSTRLEN];\n+            inet_ntop(AF_INET6, &route_adv->prefix, network_s,\n+                      INET6_ADDRSTRLEN);\n+            prefix_s = xasprintf(\"%s/%d\", network_s, route_adv->plen);\n+            inet_ntop(AF_INET6, &route_adv->nexthop, network_s,\n+                      INET6_ADDRSTRLEN);\n+            nexthop_s = xstrdup(network_s);\n+        }\n+        icsbrec_route_set_ip_prefix(isb_route, prefix_s);\n+        icsbrec_route_set_nexthop(isb_route, nexthop_s);\n+        icsbrec_route_set_origin(isb_route, route_adv->origin);\n+        icsbrec_route_set_route_table(isb_route, route_adv->route_table\n+                                                 ? route_adv->route_table\n+                                                 : \"\");\n+        free(prefix_s);\n+        free(nexthop_s);\n+\n+        ad_route_sync_external_ids(route_adv, isb_route);\n+\n+        hmap_remove(routes_ad, &route_adv->node);\n+        free(route_adv);\n+    }\n+}\n+\n+static void\n+build_ts_routes_to_adv(struct route_input *ic,\n+                       struct ic_router_info *ic_lr,\n+                       struct hmap *routes_ad,\n+                       struct lport_addresses *ts_port_addrs,\n+                       const struct nbrec_nb_global *nb_global,\n+                       const char *ts_route_table,\n+                       const char *route_tag,\n+                       const struct nbrec_logical_router_port *ts_lrp)\n+{\n+    const struct nbrec_logical_router *lr = ic_lr->lr;\n+\n+    /* Check static routes of the LR */\n+    for (int i = 0; i < lr->n_static_routes; i++) {\n+        const struct nbrec_logical_router_static_route *nb_route\n+            = lr->static_routes[i];\n+        struct uuid isb_uuid;\n+        if (smap_get_uuid(&nb_route->external_ids, \"ic-learned-route\",\n+                          &isb_uuid)) {\n+            /* It is a learned route */\n+            if (!add_to_routes_learned(&ic_lr->routes_learned, nb_route, lr)) {\n+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+                VLOG_WARN_RL(&rl, \"Bad format of learned route in NB: \"\n+                             \"%s -> %s. Delete it.\", nb_route->ip_prefix,\n+                             nb_route->nexthop);\n+                nbrec_logical_router_update_static_routes_delvalue(lr,\n+                    nb_route);\n+            }\n+        } else if (!strcmp(ts_route_table, nb_route->route_table)) {\n+            /* It may be a route to be advertised */\n+            add_static_to_routes_ad(routes_ad, nb_route, lr, ts_port_addrs,\n+                                    &nb_global->options, route_tag, ts_lrp);\n+        }\n+    }\n+\n+    /* Check directly-connected subnets of the LR */\n+    for (int i = 0; i < lr->n_ports; i++) {\n+        const struct nbrec_logical_router_port *lrp = lr->ports[i];\n+        if (!lrp_is_ts_port(ic, ic_lr, lrp->name)) {\n+            for (int j = 0; j < lrp->n_networks; j++) {\n+                add_network_to_routes_ad(routes_ad, lrp->networks[j], lrp,\n+                                         ts_port_addrs,\n+                                         &nb_global->options,\n+                                         lr, route_tag, ts_lrp);\n+            }\n+        } else {\n+            /* The router port of the TS port is ignored. */\n+            VLOG_DBG(\"Skip advertising direct route of lrp %s (TS port)\",\n+                     lrp->name);\n+        }\n+    }\n+\n+    /* Check loadbalancers associated with the LR */\n+    if (smap_get_bool(&nb_global->options, \"ic-route-adv-lb\", false)) {\n+        for (size_t i = 0; i < lr->n_load_balancer; i++) {\n+            const struct nbrec_load_balancer *nb_lb = lr->load_balancer[i];\n+            struct smap_node *node;\n+            SMAP_FOR_EACH (node, &nb_lb->vips) {\n+                add_lb_vip_to_routes_ad(routes_ad, node->key, nb_lb,\n+                                        ts_port_addrs,\n+                                        &nb_global->options,\n+                                        lr, route_tag, ts_lrp);\n+            }\n+        }\n+\n+        for (size_t i = 0; i < lr->n_load_balancer_group; i++) {\n+            const struct nbrec_load_balancer_group *nb_lbg =\n+                lr->load_balancer_group[i];\n+            for (size_t j = 0; j < nb_lbg->n_load_balancer; j++) {\n+                const struct nbrec_load_balancer *nb_lb =\n+                    nb_lbg->load_balancer[j];\n+                struct smap_node *node;\n+                SMAP_FOR_EACH (node, &nb_lb->vips) {\n+                    add_lb_vip_to_routes_ad(routes_ad, node->key, nb_lb,\n+                                            ts_port_addrs,\n+                                            &nb_global->options,\n+                                            lr, route_tag, ts_lrp);\n+                }\n+            }\n+        }\n+    }\n+}\n+\n+static void\n+collect_lr_routes(struct route_input *ic,\n+                  struct ic_router_info *ic_lr,\n+                  struct shash *routes_ad_by_ts,\n+                  const struct nbrec_nb_global_table *nb_global_table)\n+{\n+    const struct nbrec_nb_global *nb_global =\n+        nbrec_nb_global_table_first(nb_global_table);\n+\n+    ovs_assert(nb_global);\n+\n+    const struct icsbrec_port_binding *isb_pb;\n+    const char *lrp_name, *ts_name, *route_table, *route_tag;\n+    struct lport_addresses ts_port_addrs;\n+    const struct icnbrec_transit_switch *key;\n+    const struct nbrec_logical_router_port *lrp;\n+\n+    struct hmap *routes_ad;\n+    const struct icnbrec_transit_switch *t_sw;\n+    VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) {\n+        key = icnbrec_transit_switch_index_init_row(\n+            ic->icnbrec_transit_switch_by_name);\n+        icnbrec_transit_switch_index_set_name(key, isb_pb->transit_switch);\n+        t_sw = icnbrec_transit_switch_index_find(\n+             ic->icnbrec_transit_switch_by_name, key);\n+        icnbrec_transit_switch_index_destroy_row(key);\n+        if (!t_sw) {\n+            continue;\n+        }\n+        ts_name = t_sw->name;\n+        routes_ad = shash_find_data(routes_ad_by_ts, ts_name);\n+        if (!routes_ad) {\n+            routes_ad = xzalloc(sizeof *routes_ad);\n+            hmap_init(routes_ad);\n+            shash_add(routes_ad_by_ts, ts_name, routes_ad);\n+        }\n+\n+        if (!extract_lsp_addresses(isb_pb->address, &ts_port_addrs)) {\n+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+            VLOG_INFO_RL(&rl, \"Route sync ignores port %s on ts %s for router\"\n+                         \" %s because the addresses are invalid.\",\n+                         isb_pb->logical_port, isb_pb->transit_switch,\n+                         ic_lr->lr->name);\n+            continue;\n+        }\n+        lrp_name = get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port);\n+        lrp = get_lrp_by_lrp_name(ic->nbrec_lrp_by_name, lrp_name);\n+        if (lrp) {\n+            route_table = smap_get_def(&lrp->options, \"route_table\", \"\");\n+            route_tag = smap_get_def(&lrp->options, \"ic-route-tag\", \"\");\n+        } else {\n+            route_table = \"\";\n+            route_tag = \"\";\n+        }\n+        build_ts_routes_to_adv(ic, ic_lr, routes_ad, &ts_port_addrs,\n+                               nb_global, route_table, route_tag, lrp);\n+        destroy_lport_addresses(&ts_port_addrs);\n+    }\n+}\n+\n+static void\n+delete_orphan_ic_routes(struct route_input *ic,\n+                        const struct icsbrec_availability_zone *az)\n+{\n+    const struct icsbrec_route *isb_route, *isb_route_key =\n+        icsbrec_route_index_init_row(ic->icsbrec_route_by_az);\n+    icsbrec_route_index_set_availability_zone(isb_route_key, az);\n+\n+    const struct icnbrec_transit_switch *t_sw, *t_sw_key;\n+\n+    ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key,\n+                                  ic->icsbrec_route_by_az)\n+    {\n+        t_sw_key = icnbrec_transit_switch_index_init_row(\n+            ic->icnbrec_transit_switch_by_name);\n+        icnbrec_transit_switch_index_set_name(t_sw_key,\n+            isb_route->transit_switch);\n+        t_sw = icnbrec_transit_switch_index_find(\n+            ic->icnbrec_transit_switch_by_name, t_sw_key);\n+        icnbrec_transit_switch_index_destroy_row(t_sw_key);\n+\n+        if (!t_sw || !find_lrp_of_nexthop(ic, isb_route)) {\n+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+            VLOG_INFO_RL(&rl, \"Deleting orphan ICDB:Route: %s->%s (%s, rtb:%s,\"\n+                         \" transit switch: %s)\", isb_route->ip_prefix,\n+                         isb_route->nexthop, isb_route->origin,\n+                         isb_route->route_table, isb_route->transit_switch);\n+            icsbrec_route_delete(isb_route);\n+        }\n+    }\n+    icsbrec_route_index_destroy_row(isb_route_key);\n+}\ndiff --git a/ic/en-route.h b/ic/en-route.h\nnew file mode 100644\nindex 000000000..2365edcbf\n--- /dev/null\n+++ b/ic/en-route.h\n@@ -0,0 +1,70 @@\n+#ifndef EN_IC_ROUTE_H\n+#define EN_IC_ROUTE_H 1\n+\n+#include <config.h>\n+\n+#include <stdbool.h>\n+#include <getopt.h>\n+#include <stdlib.h>\n+#include <stdio.h>\n+#include \"vec.h\"\n+\n+/* OVN includes. */\n+#include \"lib/inc-proc-eng.h\"\n+\n+struct ed_type_route {\n+    struct hmap pb_tnlids;\n+    struct shash switch_all_local_pbs;\n+    struct shash router_all_local_pbs;\n+};\n+\n+struct ic_router_info {\n+    struct hmap_node node;\n+    const struct nbrec_logical_router *lr; /* key of hmap */\n+    struct vector isb_pbs; /* Vector of const struct icsbrec_port_binding *. */\n+    struct hmap routes_learned;\n+};\n+\n+/* Represents an interconnection route entry. */\n+struct ic_route_info {\n+    struct hmap_node node;\n+    struct in6_addr prefix;\n+    unsigned int plen;\n+    struct in6_addr nexthop;\n+    const char *origin;\n+    const char *route_table;\n+    const char *route_tag;\n+\n+    const struct nbrec_logical_router *nb_lr;\n+\n+    /* One of nb_route, nb_lrp, nb_lb is set and the other ones must be NULL.\n+     * - For a route that is learned from IC-SB, or a static route that is\n+     *   generated from a route that is configured in NB, the \"nb_route\"\n+     *   is set.\n+     * - For a route that is generated from a direct-connect subnet of\n+     *   a logical router port, the \"nb_lrp\" is set.\n+     * - For a route that is generated from a load-balancer vip of\n+     *   a logical router, the \"nb_lb\" is set. */\n+    const struct nbrec_logical_router_static_route *nb_route;\n+    const struct nbrec_logical_router_port *nb_lrp;\n+    const struct nbrec_load_balancer *nb_lb;\n+};\n+\n+struct route_input {\n+    /* Indexes */\n+    const struct icsbrec_availability_zone *runned_az;\n+    struct ovsdb_idl_index *nbrec_ls_by_name;\n+    struct ovsdb_idl_index *nbrec_port_by_name;\n+    struct ovsdb_idl_index *nbrec_lrp_by_name;\n+    struct ovsdb_idl_index *icsbrec_route_by_az;\n+    struct ovsdb_idl_index *icsbrec_route_by_ts;\n+    struct ovsdb_idl_index *icsbrec_route_by_ts_az;\n+    struct ovsdb_idl_index *icsbrec_port_binding_by_az;\n+    struct ovsdb_idl_index *icnbrec_transit_switch_by_name;\n+};\n+\n+void *en_route_init(struct engine_node *, struct engine_arg *);\n+enum engine_node_state en_route_run(struct engine_node *, void *data);\n+void en_route_cleanup(void *data);\n+\n+#endif\ndiff --git a/ic/inc-proc-ic.c b/ic/inc-proc-ic.c\nindex ac360fb9f..7399dbd57 100644\n--- a/ic/inc-proc-ic.c\n+++ b/ic/inc-proc-ic.c\n@@ -29,6 +29,7 @@\n #include \"en-ic.h\"\n #include \"en-enum-datapaths.h\"\n #include \"en-port-binding.h\"\n+#include \"en-route.h\"\n #include \"unixctl.h\"\n #include \"util.h\"\n \n@@ -162,6 +163,7 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_ic);\n static ENGINE_NODE(ic, SB_WRITE);\n static ENGINE_NODE(enum_datapaths);\n static ENGINE_NODE(port_binding, SB_WRITE);\n+static ENGINE_NODE(route);\n \n void inc_proc_ic_init(struct ovsdb_idl_loop *nb,\n                       struct ovsdb_idl_loop *sb,\n@@ -181,10 +183,18 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb,\n     engine_add_input(&en_port_binding, &en_nb_logical_router, NULL);\n     engine_add_input(&en_port_binding, &en_sb_chassis, NULL);\n \n+    engine_add_input(&en_route, &en_nb_nb_global, NULL);\n+    engine_add_input(&en_route, &en_nb_logical_switch, NULL);\n+    engine_add_input(&en_route, &en_nb_logical_router, NULL);\n+    engine_add_input(&en_route, &en_icnb_transit_switch, NULL);\n+    engine_add_input(&en_route, &en_icsb_port_binding, NULL);\n+    engine_add_input(&en_route, &en_icsb_route, NULL);\n+    engine_add_input(&en_route, &en_nb_logical_router_static_route, NULL);\n+\n     engine_add_input(&en_ic, &en_enum_datapaths, NULL);\n     engine_add_input(&en_ic, &en_port_binding, NULL);\n-    engine_add_input(&en_ic, &en_nb_nb_global, NULL);\n-    engine_add_input(&en_ic, &en_nb_logical_router_static_route, NULL);\n+    engine_add_input(&en_ic, &en_route, NULL);\n+\n     engine_add_input(&en_ic, &en_nb_logical_router, NULL);\n     engine_add_input(&en_ic, &en_nb_logical_router_port, NULL);\n     engine_add_input(&en_ic, &en_nb_logical_switch, NULL);\n@@ -210,7 +220,6 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb,\n     engine_add_input(&en_ic, &en_icsb_encap, NULL);\n     engine_add_input(&en_ic, &en_icsb_service_monitor, NULL);\n     engine_add_input(&en_ic, &en_icsb_gateway, NULL);\n-    engine_add_input(&en_ic, &en_icsb_route, NULL);\n     engine_add_input(&en_ic, &en_icsb_datapath_binding, NULL);\n \n     struct engine_arg engine_arg = {\ndiff --git a/ic/ovn-ic.c b/ic/ovn-ic.c\nindex 7bde85854..c880244e2 100644\n--- a/ic/ovn-ic.c\n+++ b/ic/ovn-ic.c\n@@ -577,14 +577,15 @@ gateway_run(struct engine_context *ctx,\n }\n \n const struct nbrec_logical_router_port *\n-get_lrp_by_lrp_name(struct ic_input *ic, const char *lrp_name)\n+get_lrp_by_lrp_name(struct ovsdb_idl_index *nbrec_lrp_by_name,\n+                    const char *lrp_name)\n {\n     const struct nbrec_logical_router_port *lrp;\n     const struct nbrec_logical_router_port *lrp_key =\n-        nbrec_logical_router_port_index_init_row(ic->nbrec_lrp_by_name);\n+        nbrec_logical_router_port_index_init_row(nbrec_lrp_by_name);\n     nbrec_logical_router_port_index_set_name(lrp_key, lrp_name);\n     lrp =\n-        nbrec_logical_router_port_index_find(ic->nbrec_lrp_by_name, lrp_key);\n+        nbrec_logical_router_port_index_find(nbrec_lrp_by_name, lrp_key);\n     nbrec_logical_router_port_index_destroy_row(lrp_key);\n \n     return lrp;\n@@ -643,1297 +644,6 @@ find_sb_pb_by_name(struct ovsdb_idl_index *sbrec_port_binding_by_name,\n     return pb;\n }\n \n-struct ic_router_info {\n-    struct hmap_node node;\n-    const struct nbrec_logical_router *lr; /* key of hmap */\n-    struct vector isb_pbs; /* Vector of const struct icsbrec_port_binding *. */\n-    struct hmap routes_learned;\n-};\n-\n-/* Represents an interconnection route entry. */\n-struct ic_route_info {\n-    struct hmap_node node;\n-    struct in6_addr prefix;\n-    unsigned int plen;\n-    struct in6_addr nexthop;\n-    const char *origin;\n-    const char *route_table;\n-    const char *route_tag;\n-\n-    const struct nbrec_logical_router *nb_lr;\n-\n-    /* One of nb_route, nb_lrp, nb_lb is set and the other ones must be NULL.\n-     * - For a route that is learned from IC-SB, or a static route that is\n-     *   generated from a route that is configured in NB, the \"nb_route\"\n-     *   is set.\n-     * - For a route that is generated from a direct-connect subnet of\n-     *   a logical router port, the \"nb_lrp\" is set.\n-     * - For a route that is generated from a load-balancer vip of\n-     *   a logical router, the \"nb_lb\" is set. */\n-    const struct nbrec_logical_router_static_route *nb_route;\n-    const struct nbrec_logical_router_port *nb_lrp;\n-    const struct nbrec_load_balancer *nb_lb;\n-};\n-\n-static uint32_t\n-ic_route_hash(const struct in6_addr *prefix, unsigned int plen,\n-              const struct in6_addr *nexthop, const char *origin,\n-              const char *route_table)\n-{\n-    uint32_t basis = hash_bytes(prefix, sizeof *prefix, (uint32_t)plen);\n-    basis = hash_string(origin, basis);\n-    basis = hash_string(route_table, basis);\n-    return hash_bytes(nexthop, sizeof *nexthop, basis);\n-}\n-\n-static struct ic_route_info *\n-ic_route_find(struct hmap *routes, const struct in6_addr *prefix,\n-              unsigned int plen, const struct in6_addr *nexthop,\n-              const char *origin, const char *route_table, uint32_t hash)\n-{\n-    struct ic_route_info *r;\n-    if (!hash) {\n-        hash = ic_route_hash(prefix, plen, nexthop, origin, route_table);\n-    }\n-    HMAP_FOR_EACH_WITH_HASH (r, node, hash, routes) {\n-        if (ipv6_addr_equals(&r->prefix, prefix) &&\n-            r->plen == plen &&\n-            ipv6_addr_equals(&r->nexthop, nexthop) &&\n-            !strcmp(r->origin, origin) &&\n-            !strcmp(r->route_table ? r->route_table : \"\", route_table)) {\n-            return r;\n-        }\n-    }\n-    return NULL;\n-}\n-\n-static struct ic_router_info *\n-ic_router_find(struct hmap *ic_lrs, const struct nbrec_logical_router *lr)\n-{\n-    struct ic_router_info *ic_lr;\n-    HMAP_FOR_EACH_WITH_HASH (ic_lr, node, uuid_hash(&lr->header_.uuid),\n-                             ic_lrs) {\n-        if (ic_lr->lr == lr) {\n-           return ic_lr;\n-        }\n-    }\n-    return NULL;\n-}\n-\n-static bool\n-parse_route(const char *s_prefix, const char *s_nexthop,\n-            struct in6_addr *prefix, unsigned int *plen,\n-            struct in6_addr *nexthop)\n-{\n-    if (!ip46_parse_cidr(s_prefix, prefix, plen)) {\n-        return false;\n-    }\n-\n-    unsigned int nlen;\n-    if (strcmp(s_nexthop, \"discard\") &&\n-        !ip46_parse_cidr(s_nexthop, nexthop, &nlen)) {\n-        return false;\n-    }\n-\n-    /* Do not learn routes with link-local next hop. */\n-    return !in6_is_lla(nexthop);\n-}\n-\n-/* Return false if can't be added due to bad format. */\n-static bool\n-add_to_routes_learned(struct hmap *routes_learned,\n-                      const struct nbrec_logical_router_static_route *nb_route,\n-                      const struct nbrec_logical_router *nb_lr)\n-{\n-    struct in6_addr prefix, nexthop;\n-    unsigned int plen;\n-    if (!parse_route(nb_route->ip_prefix, nb_route->nexthop,\n-                     &prefix, &plen, &nexthop)) {\n-        return false;\n-    }\n-    const char *origin = smap_get_def(&nb_route->options, \"origin\", \"\");\n-    if (ic_route_find(routes_learned, &prefix, plen, &nexthop, origin,\n-                      nb_route->route_table, 0)) {\n-        /* Route was added to learned on previous iteration. */\n-        return true;\n-    }\n-\n-    struct ic_route_info *ic_route = xzalloc(sizeof *ic_route);\n-    ic_route->prefix = prefix;\n-    ic_route->plen = plen;\n-    ic_route->nexthop = nexthop;\n-    ic_route->nb_route = nb_route;\n-    ic_route->origin = origin;\n-    ic_route->route_table = nb_route->route_table;\n-    ic_route->nb_lr = nb_lr;\n-    hmap_insert(routes_learned, &ic_route->node,\n-                ic_route_hash(&prefix, plen, &nexthop, origin,\n-                              nb_route->route_table));\n-    return true;\n-}\n-\n-static bool\n-get_nexthop_from_lport_addresses(bool is_v4,\n-                                 const struct lport_addresses *laddr,\n-                                 struct in6_addr *nexthop)\n-{\n-    if (is_v4) {\n-        if (!laddr->n_ipv4_addrs) {\n-            return false;\n-        }\n-        in6_addr_set_mapped_ipv4(nexthop, laddr->ipv4_addrs[0].addr);\n-        return true;\n-    }\n-\n-    /* ipv6 */\n-    if (laddr->n_ipv6_addrs) {\n-        *nexthop = laddr->ipv6_addrs[0].addr;\n-        return true;\n-    }\n-\n-    /* ipv6 link local */\n-    in6_generate_lla(laddr->ea, nexthop);\n-    return true;\n-}\n-\n-static bool\n-prefix_is_filtered(struct in6_addr *prefix,\n-                   unsigned int plen,\n-                   const struct nbrec_logical_router *nb_lr,\n-                   const struct nbrec_logical_router_port *ts_lrp,\n-                   bool is_advertisement)\n-{\n-    struct ds filter_list = DS_EMPTY_INITIALIZER;\n-    const char *filter_direction = is_advertisement ? \"ic-route-filter-adv\" :\n-                                                      \"ic-route-filter-learn\";\n-    if (ts_lrp) {\n-        const char *lrp_route_filter = smap_get(&ts_lrp->options,\n-                                                filter_direction);\n-        if (lrp_route_filter) {\n-            ds_put_format(&filter_list, \"%s,\", lrp_route_filter);\n-        }\n-    }\n-    const char *lr_route_filter = smap_get(&nb_lr->options,\n-                                           filter_direction);\n-    if (lr_route_filter) {\n-        ds_put_format(&filter_list, \"%s,\", lr_route_filter);\n-    }\n-\n-    struct sset prefix_set = SSET_INITIALIZER(&prefix_set);\n-    sset_from_delimited_string(&prefix_set, ds_cstr(&filter_list), \",\");\n-\n-    bool matched = true;\n-    if (!sset_is_empty(&prefix_set)) {\n-        matched = find_prefix_in_set(prefix, plen, &prefix_set,\n-                                     filter_direction);\n-    }\n-\n-    ds_destroy(&filter_list);\n-    sset_destroy(&prefix_set);\n-    return matched;\n-}\n-\n-static bool\n-prefix_is_deny_filtered(struct in6_addr *prefix,\n-                        unsigned int plen,\n-                        const struct smap *nb_options,\n-                        const struct nbrec_logical_router *nb_lr,\n-                        const struct nbrec_logical_router_port *ts_lrp,\n-                        bool is_advertisement)\n-{\n-    struct ds deny_list = DS_EMPTY_INITIALIZER;\n-    const char *deny_key = is_advertisement ? \"ic-route-deny-adv\" :\n-                                              \"ic-route-deny-learn\";\n-\n-    if (ts_lrp) {\n-        const char *lrp_deny_filter = smap_get(&ts_lrp->options, deny_key);\n-        if (lrp_deny_filter) {\n-            ds_put_format(&deny_list, \"%s,\", lrp_deny_filter);\n-        }\n-    }\n-\n-    if (nb_lr) {\n-        const char *lr_deny_filter = smap_get(&nb_lr->options, deny_key);\n-        if (lr_deny_filter) {\n-            ds_put_format(&deny_list, \"%s,\", lr_deny_filter);\n-        }\n-    }\n-\n-    if (nb_options) {\n-        const char *global_deny = smap_get(nb_options, \"ic-route-denylist\");\n-        if (!global_deny || !global_deny[0]) {\n-            global_deny = smap_get(nb_options, \"ic-route-blacklist\");\n-        }\n-        if (global_deny && global_deny[0]) {\n-            ds_put_format(&deny_list, \"%s,\", global_deny);\n-        }\n-    }\n-\n-    struct sset prefix_set = SSET_INITIALIZER(&prefix_set);\n-    sset_from_delimited_string(&prefix_set, ds_cstr(&deny_list), \",\");\n-\n-    bool denied = false;\n-    if (!sset_is_empty(&prefix_set)) {\n-        denied = find_prefix_in_set(prefix, plen, &prefix_set, deny_key);\n-    }\n-\n-    ds_destroy(&deny_list);\n-    sset_destroy(&prefix_set);\n-    return denied;\n-}\n-\n-static bool\n-route_need_advertise(const char *policy,\n-                     struct in6_addr *prefix,\n-                     unsigned int plen,\n-                     const struct smap *nb_options,\n-                     const struct nbrec_logical_router *nb_lr,\n-                     const struct nbrec_logical_router_port *ts_lrp)\n-{\n-    if (!smap_get_bool(nb_options, \"ic-route-adv\", false)) {\n-        return false;\n-    }\n-\n-    if (plen == 0 &&\n-        !smap_get_bool(nb_options, \"ic-route-adv-default\", false)) {\n-        return false;\n-    }\n-\n-    if (policy && !strcmp(policy, \"src-ip\")) {\n-        return false;\n-    }\n-\n-    if (prefix_is_link_local(prefix, plen)) {\n-        return false;\n-    }\n-\n-    if (prefix_is_deny_filtered(prefix, plen, nb_options,\n-                                nb_lr, ts_lrp, true)) {\n-        return false;\n-    }\n-\n-    if (!prefix_is_filtered(prefix, plen, nb_lr, ts_lrp, true)) {\n-        return false;\n-    }\n-\n-    return true;\n-}\n-\n-static void\n-add_to_routes_ad(struct hmap *routes_ad, const struct in6_addr prefix,\n-                 unsigned int plen, const struct in6_addr nexthop,\n-                 const char *origin, const char *route_table,\n-                 const struct nbrec_logical_router_port *nb_lrp,\n-                 const struct nbrec_logical_router_static_route *nb_route,\n-                 const struct nbrec_logical_router *nb_lr,\n-                 const struct nbrec_load_balancer *nb_lb,\n-                 const char *route_tag)\n-{\n-    ovs_assert(nb_route || nb_lrp || nb_lb);\n-\n-    if (route_table == NULL) {\n-        route_table = \"\";\n-    }\n-\n-    uint hash = ic_route_hash(&prefix, plen, &nexthop, origin, route_table);\n-\n-    if (!ic_route_find(routes_ad, &prefix, plen, &nexthop, origin,\n-                       route_table, hash)) {\n-        struct ic_route_info *ic_route = xzalloc(sizeof *ic_route);\n-        ic_route->prefix = prefix;\n-        ic_route->plen = plen;\n-        ic_route->nexthop = nexthop;\n-        ic_route->nb_route = nb_route;\n-        ic_route->origin = origin;\n-        ic_route->route_table = route_table;\n-        ic_route->nb_lrp = nb_lrp;\n-        ic_route->nb_lr = nb_lr;\n-        ic_route->nb_lb = nb_lb;\n-        ic_route->route_tag = route_tag;\n-        hmap_insert(routes_ad, &ic_route->node, hash);\n-    } else {\n-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n-        const char *msg_fmt = \"Duplicate %s route advertisement was \"\n-                              \"suppressed! NB %s uuid: \"UUID_FMT;\n-        if (nb_route) {\n-            VLOG_WARN_RL(&rl, msg_fmt, origin, \"route\",\n-                         UUID_ARGS(&nb_route->header_.uuid));\n-        } else if (nb_lb) {\n-            VLOG_WARN_RL(&rl, msg_fmt, origin, \"loadbalancer\",\n-                         UUID_ARGS(&nb_lb->header_.uuid));\n-        } else {\n-            VLOG_WARN_RL(&rl, msg_fmt, origin, \"lrp\",\n-                         UUID_ARGS(&nb_lrp->header_.uuid));\n-        }\n-    }\n-}\n-\n-static void\n-add_static_to_routes_ad(\n-    struct hmap *routes_ad,\n-    const struct nbrec_logical_router_static_route *nb_route,\n-    const struct nbrec_logical_router *nb_lr,\n-    const struct lport_addresses *nexthop_addresses,\n-    const struct smap *nb_options,\n-    const char *route_tag,\n-    const struct nbrec_logical_router_port *ts_lrp)\n-{\n-    struct in6_addr prefix, nexthop;\n-    unsigned int plen;\n-    if (!parse_route(nb_route->ip_prefix, nb_route->nexthop,\n-                     &prefix, &plen, &nexthop)) {\n-        return;\n-    }\n-\n-    if (!route_need_advertise(nb_route->policy, &prefix, plen, nb_options,\n-                              nb_lr, ts_lrp)) {\n-        return;\n-    }\n-\n-    if (!get_nexthop_from_lport_addresses(IN6_IS_ADDR_V4MAPPED(&prefix),\n-                                          nexthop_addresses,\n-                                          &nexthop)) {\n-        return;\n-    }\n-\n-    if (VLOG_IS_DBG_ENABLED()) {\n-        struct ds msg = DS_EMPTY_INITIALIZER;\n-\n-        ds_put_format(&msg, \"Advertising static route: %s -> %s, ic nexthop: \",\n-                      nb_route->ip_prefix, nb_route->nexthop);\n-\n-        if (IN6_IS_ADDR_V4MAPPED(&nexthop)) {\n-            ds_put_format(&msg, IP_FMT,\n-                          IP_ARGS(in6_addr_get_mapped_ipv4(&nexthop)));\n-        } else {\n-            ipv6_format_addr(&nexthop, &msg);\n-        }\n-\n-        ds_put_format(&msg, \", route_table: %s\", nb_route->route_table[0]\n-                                                 ? nb_route->route_table\n-                                                 : \"<main>\");\n-\n-        VLOG_DBG(\"%s\", ds_cstr(&msg));\n-        ds_destroy(&msg);\n-    }\n-\n-    add_to_routes_ad(routes_ad, prefix, plen, nexthop, ROUTE_ORIGIN_STATIC,\n-                     nb_route->route_table, NULL, nb_route, nb_lr,\n-                     NULL, route_tag);\n-}\n-\n-static void\n-add_network_to_routes_ad(struct hmap *routes_ad, const char *network,\n-                         const struct nbrec_logical_router_port *nb_lrp,\n-                         const struct lport_addresses *nexthop_addresses,\n-                         const struct smap *nb_options,\n-                         const struct nbrec_logical_router *nb_lr,\n-                         const char *route_tag,\n-                         const struct nbrec_logical_router_port *ts_lrp)\n-{\n-    struct in6_addr prefix, nexthop;\n-    unsigned int plen;\n-    if (!ip46_parse_cidr(network, &prefix, &plen)) {\n-        return;\n-    }\n-\n-    if (!route_need_advertise(NULL, &prefix, plen, nb_options,\n-                              nb_lr, ts_lrp)) {\n-        VLOG_DBG(\"Route ad: skip network %s of lrp %s.\",\n-                 network, nb_lrp->name);\n-        return;\n-    }\n-\n-    if (!get_nexthop_from_lport_addresses(IN6_IS_ADDR_V4MAPPED(&prefix),\n-                                          nexthop_addresses,\n-                                          &nexthop)) {\n-        return;\n-    }\n-\n-    if (VLOG_IS_DBG_ENABLED()) {\n-        struct ds msg = DS_EMPTY_INITIALIZER;\n-\n-        ds_put_format(&msg, \"Adding direct network route to <main> routing \"\n-                      \"table: %s of lrp %s, nexthop \", network, nb_lrp->name);\n-\n-        if (IN6_IS_ADDR_V4MAPPED(&nexthop)) {\n-            ds_put_format(&msg, IP_FMT,\n-                          IP_ARGS(in6_addr_get_mapped_ipv4(&nexthop)));\n-        } else {\n-            ipv6_format_addr(&nexthop, &msg);\n-        }\n-\n-        VLOG_DBG(\"%s\", ds_cstr(&msg));\n-        ds_destroy(&msg);\n-    }\n-\n-    /* directly-connected routes go to <main> route table */\n-    add_to_routes_ad(routes_ad, prefix, plen, nexthop, ROUTE_ORIGIN_CONNECTED,\n-                     NULL, nb_lrp, NULL, nb_lr, NULL, route_tag);\n-}\n-\n-static void\n-add_lb_vip_to_routes_ad(struct hmap *routes_ad, const char *vip_key,\n-                        const struct nbrec_load_balancer *nb_lb,\n-                        const struct lport_addresses *nexthop_addresses,\n-                        const struct smap *nb_options,\n-                        const struct nbrec_logical_router *nb_lr,\n-                        const char *route_tag,\n-                        const struct nbrec_logical_router_port *ts_lrp)\n-{\n-    char *vip_str = NULL;\n-    struct in6_addr vip_ip, nexthop;\n-    uint16_t vip_port;\n-    int addr_family;\n-    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n-\n-    if (!ip_address_and_port_from_lb_key(vip_key, &vip_str, &vip_ip,\n-                                         &vip_port, &addr_family)) {\n-        VLOG_WARN_RL(&rl, \"Route ad: Parsing failed for lb vip %s\", vip_key);\n-        return;\n-    }\n-    if (vip_str == NULL) {\n-        return;\n-    }\n-    unsigned int plen = (addr_family == AF_INET) ? 32 : 128;\n-    if (!route_need_advertise(NULL, &vip_ip, plen, nb_options,\n-                              nb_lr, ts_lrp)) {\n-        VLOG_DBG(\"Route ad: skip lb vip %s.\", vip_key);\n-        goto out;\n-    }\n-    if (!get_nexthop_from_lport_addresses(IN6_IS_ADDR_V4MAPPED(&vip_ip),\n-                                          nexthop_addresses,\n-                                          &nexthop)) {\n-        VLOG_WARN_RL(&rl, \"Route ad: failed to get nexthop for lb vip\");\n-        goto out;\n-    }\n-\n-    if (VLOG_IS_DBG_ENABLED()) {\n-        struct ds msg = DS_EMPTY_INITIALIZER;\n-\n-        ds_put_format(&msg, \"Adding lb vip route to <main> routing \"\n-                      \"table: %s, nexthop \", vip_str);\n-\n-        if (IN6_IS_ADDR_V4MAPPED(&nexthop)) {\n-            ds_put_format(&msg, IP_FMT,\n-                          IP_ARGS(in6_addr_get_mapped_ipv4(&nexthop)));\n-        } else {\n-            ipv6_format_addr(&nexthop, &msg);\n-        }\n-\n-        VLOG_DBG(\"%s\", ds_cstr(&msg));\n-        ds_destroy(&msg);\n-    }\n-\n-    /* Lb vip routes go to <main> route table */\n-    add_to_routes_ad(routes_ad, vip_ip, plen, nexthop, ROUTE_ORIGIN_LB,\n-                     NULL, NULL, NULL, nb_lr, nb_lb, route_tag);\n-out:\n-    free(vip_str);\n-}\n-\n-static bool\n-route_has_local_gw(const struct nbrec_logical_router *lr,\n-                   const char *route_table, const char *ip_prefix) {\n-\n-    const struct nbrec_logical_router_static_route *route;\n-    for (int i = 0; i < lr->n_static_routes; i++) {\n-        route = lr->static_routes[i];\n-        if (!smap_get(&route->external_ids, \"ic-learned-route\") &&\n-            !strcmp(route->route_table, route_table) &&\n-            !strcmp(route->ip_prefix, ip_prefix)) {\n-            return true;\n-        }\n-    }\n-    return false;\n-}\n-\n-static bool\n-lrp_has_neighbor_in_ts(const struct nbrec_logical_router_port *lrp,\n-                       struct in6_addr *nexthop)\n-{\n-    if (!lrp || !nexthop) {\n-        return false;\n-    }\n-\n-    struct lport_addresses lrp_networks;\n-    if (!extract_lrp_networks(lrp, &lrp_networks)) {\n-        destroy_lport_addresses(&lrp_networks);\n-        return false;\n-    }\n-\n-    if (IN6_IS_ADDR_V4MAPPED(nexthop)) {\n-        ovs_be32 neigh_prefix_v4 = in6_addr_get_mapped_ipv4(nexthop);\n-        for (size_t i = 0; i < lrp_networks.n_ipv4_addrs; i++) {\n-            struct ipv4_netaddr address = lrp_networks.ipv4_addrs[i];\n-            if (address.network == (neigh_prefix_v4 & address.mask)) {\n-                destroy_lport_addresses(&lrp_networks);\n-                return true;\n-            }\n-        }\n-    } else {\n-        for (size_t i = 0; i < lrp_networks.n_ipv6_addrs; i++) {\n-            struct ipv6_netaddr address = lrp_networks.ipv6_addrs[i];\n-            struct in6_addr neigh_prefix = ipv6_addr_bitand(nexthop,\n-                                                            &address.mask);\n-            if (ipv6_addr_equals(&address.network, &neigh_prefix)) {\n-                destroy_lport_addresses(&lrp_networks);\n-                return true;\n-            }\n-        }\n-    }\n-\n-    destroy_lport_addresses(&lrp_networks);\n-    return false;\n-}\n-\n-static bool\n-route_matches_local_lb(const struct nbrec_load_balancer *nb_lb,\n-                       const char *ip_prefix)\n-{\n-    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n-    struct in6_addr prefix;\n-    unsigned int plen;\n-\n-    if (!ip46_parse_cidr(ip_prefix, &prefix, &plen)) {\n-        return false;\n-    }\n-\n-    struct smap_node *node;\n-    SMAP_FOR_EACH (node, &nb_lb->vips) {\n-        char *vip_str = NULL;\n-        struct in6_addr vip_ip;\n-        uint16_t vip_port;\n-        int addr_family;\n-        if (ip_address_and_port_from_lb_key(node->key, &vip_str,\n-                                            &vip_ip, &vip_port,\n-                                            &addr_family)) {\n-            if (IN6_IS_ADDR_V4MAPPED(&prefix) && addr_family == AF_INET) {\n-                ovs_be32 vip = in6_addr_get_mapped_ipv4(&vip_ip);\n-                ovs_be32 mask = be32_prefix_mask(plen);\n-\n-                if ((vip & mask) == in6_addr_get_mapped_ipv4(&prefix)) {\n-                    free(vip_str);\n-                    return true;\n-                }\n-            } else if (!IN6_IS_ADDR_V4MAPPED(&prefix)\n-                       && addr_family == AF_INET6) {\n-                struct in6_addr mask = ipv6_create_mask(plen);\n-                struct in6_addr vip_prefix = ipv6_addr_bitand(&vip_ip, &mask);\n-                if (ipv6_addr_equals(&prefix, &vip_prefix)) {\n-                    free(vip_str);\n-                    return true;\n-                }\n-            }\n-            free(vip_str);\n-        } else {\n-            VLOG_WARN_RL(&rl,\n-                         \"Route learn: Parsing failed for local lb vip %s\",\n-                         node->key);\n-        }\n-    }\n-    return false;\n-}\n-\n-static bool\n-route_need_learn(const struct nbrec_logical_router *lr,\n-                 const struct icsbrec_route *isb_route,\n-                 struct in6_addr *prefix, unsigned int plen,\n-                 const struct smap *nb_options,\n-                 const struct nbrec_logical_router_port *ts_lrp,\n-                 struct in6_addr *nexthop)\n-{\n-    if (!smap_get_bool(nb_options, \"ic-route-learn\", false)) {\n-        return false;\n-    }\n-\n-    if (plen == 0 &&\n-        !smap_get_bool(nb_options, \"ic-route-learn-default\", false)) {\n-        return false;\n-    }\n-\n-    if (!strcmp(isb_route->origin, ROUTE_ORIGIN_LB) &&\n-        !smap_get_bool(nb_options, \"ic-route-learn-lb\", false)) {\n-        return false;\n-    }\n-\n-    if (!lrouter_is_enabled(lr)) {\n-        return false;\n-    }\n-\n-    if (prefix_is_link_local(prefix, plen)) {\n-        return false;\n-    }\n-\n-    if (prefix_is_deny_filtered(prefix, plen, nb_options, lr, ts_lrp, false)) {\n-        return false;\n-    }\n-\n-    if (!prefix_is_filtered(prefix, plen, lr, ts_lrp, false)) {\n-        return false;\n-    }\n-\n-    if (route_has_local_gw(lr, isb_route->route_table, isb_route->ip_prefix)) {\n-        VLOG_DBG(\"Skip learning %s (rtb:%s) route, as we've got one with \"\n-                 \"local GW\", isb_route->ip_prefix, isb_route->route_table);\n-        return false;\n-    }\n-\n-    if (!lrp_has_neighbor_in_ts(ts_lrp, nexthop)) {\n-        return false;\n-    }\n-\n-    for (size_t i = 0; i < lr->n_load_balancer; i++) {\n-        if (route_matches_local_lb(lr->load_balancer[i],\n-                                   isb_route->ip_prefix)) {\n-            VLOG_DBG(\"Skip learning %s (rtb:%s) route, as we've got local\"\n-                     \" LB with matching VIP\", isb_route->ip_prefix,\n-                     isb_route->route_table);\n-            return false;\n-        }\n-    }\n-    for (size_t i = 0; i < lr->n_load_balancer_group; i++) {\n-        const struct nbrec_load_balancer_group *nb_lbg =\n-            lr->load_balancer_group[i];\n-        for (size_t j = 0; j < nb_lbg->n_load_balancer; j++) {\n-            if (route_matches_local_lb(nb_lbg->load_balancer[j],\n-                                       isb_route->ip_prefix)) {\n-                VLOG_DBG(\"Skip learning %s (rtb:%s) route, as we've got local\"\n-                         \" LB with matching VIP\", isb_route->ip_prefix,\n-                         isb_route->route_table);\n-                return false;\n-            }\n-        }\n-    }\n-\n-    return true;\n-}\n-\n-static const char *\n-get_lrp_name_by_ts_port_name(struct ic_input *ic, const char *ts_port_name)\n-{\n-    const struct nbrec_logical_switch_port *nb_lsp;\n-\n-    nb_lsp = get_lsp_by_ts_port_name(ic->nbrec_port_by_name, ts_port_name);\n-    if (!nb_lsp) {\n-        return NULL;\n-    }\n-\n-    return smap_get(&nb_lsp->options, \"router-port\");\n-}\n-\n-static const struct nbrec_logical_router_port *\n-find_lrp_of_nexthop(struct ic_input *ic,\n-                    const struct icsbrec_route *isb_route)\n-{\n-    const struct nbrec_logical_router_port *lrp;\n-    const struct nbrec_logical_switch *ls;\n-    ls = find_ts_in_nb(ic->nbrec_ls_by_name, isb_route->transit_switch);\n-    if (!ls) {\n-        return NULL;\n-    }\n-\n-    struct in6_addr nexthop;\n-    if (!ip46_parse(isb_route->nexthop, &nexthop)) {\n-        return NULL;\n-    }\n-\n-    for (size_t i = 0; i < ls->n_ports; i++) {\n-        char *lsp_name = ls->ports[i]->name;\n-        const char *lrp_name = get_lrp_name_by_ts_port_name(ic,\n-                                                            lsp_name);\n-        if (!lrp_name) {\n-            continue;\n-        }\n-\n-        lrp = get_lrp_by_lrp_name(ic, lrp_name);\n-        if (!lrp) {\n-            continue;\n-        }\n-\n-        struct lport_addresses lrp_networks;\n-        if (!extract_lrp_networks(lrp, &lrp_networks)) {\n-            destroy_lport_addresses(&lrp_networks);\n-            continue;\n-        }\n-\n-        if (IN6_IS_ADDR_V4MAPPED(&nexthop)) {\n-            ovs_be32 nexthop_v4 = in6_addr_get_mapped_ipv4(&nexthop);\n-            for (size_t i_v4 = 0; i_v4  < lrp_networks.n_ipv4_addrs; i_v4++) {\n-                struct ipv4_netaddr address = lrp_networks.ipv4_addrs[i_v4];\n-                if (address.addr == nexthop_v4) {\n-                    destroy_lport_addresses(&lrp_networks);\n-                    return lrp;\n-                }\n-            }\n-        } else {\n-            for (size_t i_v6 = 0; i_v6 < lrp_networks.n_ipv6_addrs; i_v6++) {\n-                struct ipv6_netaddr address = lrp_networks.ipv6_addrs[i_v6];\n-                struct in6_addr nexthop_v6 = ipv6_addr_bitand(&nexthop,\n-                                                              &address.mask);\n-                if (ipv6_addr_equals(&address.network, &nexthop_v6)) {\n-                    destroy_lport_addresses(&lrp_networks);\n-                    return lrp;\n-                }\n-            }\n-        }\n-        destroy_lport_addresses(&lrp_networks);\n-    }\n-\n-    return NULL;\n-}\n-\n-static bool\n-lrp_is_ts_port(struct ic_input *ic, struct ic_router_info *ic_lr,\n-               const char *lrp_name)\n-{\n-    const struct icsbrec_port_binding *isb_pb;\n-    const char *ts_lrp_name;\n-    VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) {\n-        ts_lrp_name = get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port);\n-        if (!strcmp(ts_lrp_name, lrp_name)) {\n-            return true;\n-        }\n-    }\n-    return false;\n-}\n-\n-static void\n-sync_learned_routes(struct engine_context *ctx,\n-                    struct ic_input *ic,\n-                    struct ic_router_info *ic_lr)\n-{\n-    ovs_assert(ctx->ovnnb_idl_txn);\n-    const struct icsbrec_route *isb_route, *isb_route_key;\n-\n-    const struct nbrec_nb_global *nb_global =\n-        nbrec_nb_global_table_first(ic->nbrec_nb_global_table);\n-    ovs_assert(nb_global);\n-\n-    const char *lrp_name, *ts_route_table, *route_filter_tag;\n-    const struct icsbrec_port_binding *isb_pb;\n-    const struct nbrec_logical_router_port *lrp;\n-    VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) {\n-        if (!strcmp(isb_pb->address, \"\")) {\n-            continue;\n-        }\n-        lrp_name = get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port);\n-        lrp = get_lrp_by_lrp_name(ic, lrp_name);\n-        if (lrp) {\n-            ts_route_table = smap_get_def(&lrp->options, \"route_table\", \"\");\n-            route_filter_tag = smap_get_def(&lrp->options,\n-                                            \"ic-route-filter-tag\", \"\");\n-        } else {\n-            ts_route_table = \"\";\n-            route_filter_tag = \"\";\n-        }\n-\n-        isb_route_key = icsbrec_route_index_init_row(ic->icsbrec_route_by_ts);\n-        icsbrec_route_index_set_transit_switch(isb_route_key,\n-                                               isb_pb->transit_switch);\n-\n-        ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key,\n-                                      ic->icsbrec_route_by_ts) {\n-            /* Filters ICSB routes, skipping those that either belong to\n-             * current logical router or are legacy routes from the current\n-             * availability zone (withoud lr-id).\n-             */\n-            const char *lr_id = smap_get(&isb_route->external_ids, \"lr-id\");\n-            struct uuid lr_uuid;\n-            if (lr_id) {\n-                if (!uuid_from_string(&lr_uuid, lr_id)\n-                    || uuid_equals(&ic_lr->lr->header_.uuid, &lr_uuid)) {\n-                    continue;\n-                }\n-            } else if (isb_route->availability_zone == ic->runned_az) {\n-                continue;\n-            }\n-\n-            const char *isb_route_tag = smap_get(&isb_route->external_ids,\n-                                                 \"ic-route-tag\");\n-            if (isb_route_tag  && !strcmp(isb_route_tag, route_filter_tag)) {\n-                VLOG_DBG(\"Skip learning route %s -> %s as its route tag \"\n-                         \"[%s] is filtered by the filter tag [%s] of TS LRP \",\n-                         isb_route->ip_prefix, isb_route->nexthop,\n-                         isb_route_tag, route_filter_tag);\n-                continue;\n-            }\n-\n-            if (isb_route->route_table[0] &&\n-                strcmp(isb_route->route_table, ts_route_table)) {\n-                if (VLOG_IS_DBG_ENABLED()) {\n-                    VLOG_DBG(\"Skip learning static route %s -> %s as either \"\n-                             \"its route table %s != %s of TS port or \",\n-                             isb_route->ip_prefix, isb_route->nexthop,\n-                             isb_route->route_table, ts_route_table);\n-                }\n-                continue;\n-            }\n-\n-            struct in6_addr prefix, nexthop;\n-            unsigned int plen;\n-            if (!parse_route(isb_route->ip_prefix, isb_route->nexthop,\n-                             &prefix, &plen, &nexthop)) {\n-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n-                VLOG_WARN_RL(&rl, \"Bad route format in IC-SB: %s -> %s. \"\n-                             \"Ignored.\", isb_route->ip_prefix,\n-                             isb_route->nexthop);\n-                continue;\n-            }\n-            if (!route_need_learn(ic_lr->lr, isb_route, &prefix, plen,\n-                                  &nb_global->options, lrp, &nexthop)) {\n-                continue;\n-            }\n-\n-            struct ic_route_info *route_learned\n-                = ic_route_find(&ic_lr->routes_learned, &prefix, plen,\n-                                &nexthop, isb_route->origin,\n-                                isb_route->route_table, 0);\n-            if (route_learned) {\n-                /* Sync external-ids */\n-                struct uuid ext_id;\n-                smap_get_uuid(&route_learned->nb_route->external_ids,\n-                              \"ic-learned-route\", &ext_id);\n-                if (!uuid_equals(&ext_id, &isb_route->header_.uuid)) {\n-                    char *uuid_s =\n-                        xasprintf(UUID_FMT,\n-                                  UUID_ARGS(&isb_route->header_.uuid));\n-                    nbrec_logical_router_static_route_update_external_ids_setkey(\n-                        route_learned->nb_route, \"ic-learned-route\", uuid_s);\n-                    free(uuid_s);\n-                }\n-                hmap_remove(&ic_lr->routes_learned, &route_learned->node);\n-                free(route_learned);\n-            } else {\n-                /* Create the missing route in NB. */\n-                const struct nbrec_logical_router_static_route *nb_route =\n-                    nbrec_logical_router_static_route_insert(\n-                        ctx->ovnnb_idl_txn);\n-                nbrec_logical_router_static_route_set_ip_prefix(nb_route,\n-                    isb_route->ip_prefix);\n-                nbrec_logical_router_static_route_set_nexthop(nb_route,\n-                    isb_route->nexthop);\n-                char *uuid_s = xasprintf(UUID_FMT,\n-                                         UUID_ARGS(&isb_route->header_.uuid));\n-                nbrec_logical_router_static_route_set_route_table(nb_route,\n-                    isb_route->route_table);\n-                nbrec_logical_router_static_route_update_external_ids_setkey(\n-                    nb_route, \"ic-learned-route\", uuid_s);\n-                nbrec_logical_router_static_route_update_options_setkey(\n-                    nb_route, \"origin\", isb_route->origin);\n-                free(uuid_s);\n-                nbrec_logical_router_update_static_routes_addvalue(ic_lr->lr,\n-                    nb_route);\n-            }\n-        }\n-        icsbrec_route_index_destroy_row(isb_route_key);\n-    }\n-\n-    /* Delete extra learned routes. */\n-    struct ic_route_info *route_learned;\n-    HMAP_FOR_EACH_SAFE (route_learned, node, &ic_lr->routes_learned) {\n-        VLOG_DBG(\"Delete route %s -> %s that is not in IC-SB from NB.\",\n-                 route_learned->nb_route->ip_prefix,\n-                 route_learned->nb_route->nexthop);\n-        nbrec_logical_router_update_static_routes_delvalue(\n-            ic_lr->lr, route_learned->nb_route);\n-        hmap_remove(&ic_lr->routes_learned, &route_learned->node);\n-        free(route_learned);\n-    }\n-}\n-\n-static void\n-ad_route_sync_external_ids(const struct ic_route_info *route_adv,\n-                           const struct icsbrec_route *isb_route)\n-{\n-    struct uuid isb_ext_id, nb_id, isb_ext_lr_id, lr_id;\n-    const char *route_tag;\n-    smap_get_uuid(&isb_route->external_ids, \"nb-id\", &isb_ext_id);\n-    smap_get_uuid(&isb_route->external_ids, \"lr-id\", &isb_ext_lr_id);\n-    nb_id = route_adv->nb_lb ? route_adv->nb_lb->header_.uuid :\n-            route_adv->nb_route ? route_adv->nb_route->header_.uuid :\n-            route_adv->nb_lrp->header_.uuid;\n-\n-    lr_id = route_adv->nb_lr->header_.uuid;\n-    if (!uuid_equals(&isb_ext_id, &nb_id)) {\n-        char *uuid_s = xasprintf(UUID_FMT, UUID_ARGS(&nb_id));\n-        icsbrec_route_update_external_ids_setkey(isb_route, \"nb-id\",\n-                                                 uuid_s);\n-        free(uuid_s);\n-    }\n-    if (!uuid_equals(&isb_ext_lr_id, &lr_id)) {\n-        char *uuid_s = xasprintf(UUID_FMT, UUID_ARGS(&lr_id));\n-        icsbrec_route_update_external_ids_setkey(isb_route, \"lr-id\",\n-                                                 uuid_s);\n-        free(uuid_s);\n-    }\n-    if (strcmp(route_adv->route_tag, \"\")) {\n-        icsbrec_route_update_external_ids_setkey(isb_route, \"ic-route-tag\",\n-                                                 route_adv->route_tag);\n-    } else {\n-        route_tag = smap_get(&isb_route->external_ids, \"ic-route-tag\");\n-        if (route_tag) {\n-            icsbrec_route_update_external_ids_delkey(isb_route,\n-                                                     \"ic-route-tag\");\n-        }\n-    }\n-}\n-\n-/* Sync routes from routes_ad to IC-SB. */\n-static void\n-advertise_routes(struct engine_context *ctx,\n-                 struct ic_input *ic,\n-                 const struct icsbrec_availability_zone *az,\n-                 const char *ts_name,\n-                 struct hmap *routes_ad)\n-{\n-    ovs_assert(ctx->ovnisb_idl_txn);\n-    const struct icsbrec_route *isb_route;\n-    const struct icsbrec_route *isb_route_key =\n-        icsbrec_route_index_init_row(ic->icsbrec_route_by_ts_az);\n-    icsbrec_route_index_set_transit_switch(isb_route_key, ts_name);\n-    icsbrec_route_index_set_availability_zone(isb_route_key, az);\n-\n-    ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key,\n-                                  ic->icsbrec_route_by_ts_az) {\n-        struct in6_addr prefix, nexthop;\n-        unsigned int plen;\n-\n-        if (!parse_route(isb_route->ip_prefix, isb_route->nexthop,\n-                         &prefix, &plen, &nexthop)) {\n-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n-            VLOG_WARN_RL(&rl, \"Bad route format in IC-SB: %s -> %s. \"\n-                         \"Delete it.\",\n-                         isb_route->ip_prefix, isb_route->nexthop);\n-            icsbrec_route_delete(isb_route);\n-            continue;\n-        }\n-        struct ic_route_info *route_adv =\n-            ic_route_find(routes_ad, &prefix, plen, &nexthop,\n-                          isb_route->origin, isb_route->route_table, 0);\n-        if (!route_adv) {\n-            /* Delete the extra route from IC-SB. */\n-            VLOG_DBG(\"Delete route %s -> %s from IC-SB, which is not found\"\n-                     \" in local routes to be advertised.\",\n-                     isb_route->ip_prefix, isb_route->nexthop);\n-            icsbrec_route_delete(isb_route);\n-        } else {\n-            ad_route_sync_external_ids(route_adv, isb_route);\n-\n-            hmap_remove(routes_ad, &route_adv->node);\n-            free(route_adv);\n-        }\n-    }\n-    icsbrec_route_index_destroy_row(isb_route_key);\n-\n-    /* Create the missing routes in IC-SB */\n-    struct ic_route_info *route_adv;\n-    HMAP_FOR_EACH_SAFE (route_adv, node, routes_ad) {\n-        isb_route = icsbrec_route_insert(ctx->ovnisb_idl_txn);\n-        icsbrec_route_set_transit_switch(isb_route, ts_name);\n-        icsbrec_route_set_availability_zone(isb_route, az);\n-\n-        char *prefix_s, *nexthop_s;\n-        if (IN6_IS_ADDR_V4MAPPED(&route_adv->prefix)) {\n-            ovs_be32 ipv4 = in6_addr_get_mapped_ipv4(&route_adv->prefix);\n-            ovs_be32 nh = in6_addr_get_mapped_ipv4(&route_adv->nexthop);\n-            prefix_s = xasprintf(IP_FMT \"/%d\", IP_ARGS(ipv4), route_adv->plen);\n-            nexthop_s = xasprintf(IP_FMT, IP_ARGS(nh));\n-        } else {\n-            char network_s[INET6_ADDRSTRLEN];\n-            inet_ntop(AF_INET6, &route_adv->prefix, network_s,\n-                      INET6_ADDRSTRLEN);\n-            prefix_s = xasprintf(\"%s/%d\", network_s, route_adv->plen);\n-            inet_ntop(AF_INET6, &route_adv->nexthop, network_s,\n-                      INET6_ADDRSTRLEN);\n-            nexthop_s = xstrdup(network_s);\n-        }\n-        icsbrec_route_set_ip_prefix(isb_route, prefix_s);\n-        icsbrec_route_set_nexthop(isb_route, nexthop_s);\n-        icsbrec_route_set_origin(isb_route, route_adv->origin);\n-        icsbrec_route_set_route_table(isb_route, route_adv->route_table\n-                                                 ? route_adv->route_table\n-                                                 : \"\");\n-        free(prefix_s);\n-        free(nexthop_s);\n-\n-        ad_route_sync_external_ids(route_adv, isb_route);\n-\n-        hmap_remove(routes_ad, &route_adv->node);\n-        free(route_adv);\n-    }\n-}\n-\n-static void\n-build_ts_routes_to_adv(struct ic_input *ic,\n-                       struct ic_router_info *ic_lr,\n-                       struct hmap *routes_ad,\n-                       struct lport_addresses *ts_port_addrs,\n-                       const struct nbrec_nb_global *nb_global,\n-                       const char *ts_route_table,\n-                       const char *route_tag,\n-                       const struct nbrec_logical_router_port *ts_lrp)\n-{\n-    const struct nbrec_logical_router *lr = ic_lr->lr;\n-\n-    /* Check static routes of the LR */\n-    for (int i = 0; i < lr->n_static_routes; i++) {\n-        const struct nbrec_logical_router_static_route *nb_route\n-            = lr->static_routes[i];\n-        struct uuid isb_uuid;\n-        if (smap_get_uuid(&nb_route->external_ids, \"ic-learned-route\",\n-                          &isb_uuid)) {\n-            /* It is a learned route */\n-            if (!add_to_routes_learned(&ic_lr->routes_learned, nb_route, lr)) {\n-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n-                VLOG_WARN_RL(&rl, \"Bad format of learned route in NB: \"\n-                             \"%s -> %s. Delete it.\", nb_route->ip_prefix,\n-                             nb_route->nexthop);\n-                nbrec_logical_router_update_static_routes_delvalue(lr,\n-                    nb_route);\n-            }\n-        } else if (!strcmp(ts_route_table, nb_route->route_table)) {\n-            /* It may be a route to be advertised */\n-            add_static_to_routes_ad(routes_ad, nb_route, lr, ts_port_addrs,\n-                                    &nb_global->options, route_tag, ts_lrp);\n-        }\n-    }\n-\n-    /* Check directly-connected subnets of the LR */\n-    for (int i = 0; i < lr->n_ports; i++) {\n-        const struct nbrec_logical_router_port *lrp = lr->ports[i];\n-        if (!lrp_is_ts_port(ic, ic_lr, lrp->name)) {\n-            for (int j = 0; j < lrp->n_networks; j++) {\n-                add_network_to_routes_ad(routes_ad, lrp->networks[j], lrp,\n-                                         ts_port_addrs,\n-                                         &nb_global->options,\n-                                         lr, route_tag, ts_lrp);\n-            }\n-        } else {\n-            /* The router port of the TS port is ignored. */\n-            VLOG_DBG(\"Skip advertising direct route of lrp %s (TS port)\",\n-                     lrp->name);\n-        }\n-    }\n-\n-    /* Check loadbalancers associated with the LR */\n-    if (smap_get_bool(&nb_global->options, \"ic-route-adv-lb\", false)) {\n-        for (size_t i = 0; i < lr->n_load_balancer; i++) {\n-            const struct nbrec_load_balancer *nb_lb = lr->load_balancer[i];\n-            struct smap_node *node;\n-            SMAP_FOR_EACH (node, &nb_lb->vips) {\n-                add_lb_vip_to_routes_ad(routes_ad, node->key, nb_lb,\n-                                        ts_port_addrs,\n-                                        &nb_global->options,\n-                                        lr, route_tag, ts_lrp);\n-            }\n-        }\n-\n-        for (size_t i = 0; i < lr->n_load_balancer_group; i++) {\n-            const struct nbrec_load_balancer_group *nb_lbg =\n-                lr->load_balancer_group[i];\n-            for (size_t j = 0; j < nb_lbg->n_load_balancer; j++) {\n-                const struct nbrec_load_balancer *nb_lb =\n-                    nb_lbg->load_balancer[j];\n-                struct smap_node *node;\n-                SMAP_FOR_EACH (node, &nb_lb->vips) {\n-                    add_lb_vip_to_routes_ad(routes_ad, node->key, nb_lb,\n-                                            ts_port_addrs,\n-                                            &nb_global->options,\n-                                            lr, route_tag, ts_lrp);\n-                }\n-            }\n-        }\n-    }\n-}\n-\n-static void\n-collect_lr_routes(struct ic_input *ic,\n-                  struct ic_router_info *ic_lr,\n-                  struct shash *routes_ad_by_ts)\n-{\n-    const struct nbrec_nb_global *nb_global =\n-        nbrec_nb_global_table_first(ic->nbrec_nb_global_table);\n-\n-    ovs_assert(nb_global);\n-\n-    const struct icsbrec_port_binding *isb_pb;\n-    const char *lrp_name, *ts_name, *route_table, *route_tag;\n-    struct lport_addresses ts_port_addrs;\n-    const struct icnbrec_transit_switch *key;\n-    const struct nbrec_logical_router_port *lrp;\n-\n-    struct hmap *routes_ad;\n-    const struct icnbrec_transit_switch *t_sw;\n-    VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) {\n-        key = icnbrec_transit_switch_index_init_row(\n-            ic->icnbrec_transit_switch_by_name);\n-        icnbrec_transit_switch_index_set_name(key, isb_pb->transit_switch);\n-        t_sw = icnbrec_transit_switch_index_find(\n-             ic->icnbrec_transit_switch_by_name, key);\n-        icnbrec_transit_switch_index_destroy_row(key);\n-        if (!t_sw) {\n-            continue;\n-        }\n-        ts_name = t_sw->name;\n-        routes_ad = shash_find_data(routes_ad_by_ts, ts_name);\n-        if (!routes_ad) {\n-            routes_ad = xzalloc(sizeof *routes_ad);\n-            hmap_init(routes_ad);\n-            shash_add(routes_ad_by_ts, ts_name, routes_ad);\n-        }\n-\n-        if (!extract_lsp_addresses(isb_pb->address, &ts_port_addrs)) {\n-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n-            VLOG_INFO_RL(&rl, \"Route sync ignores port %s on ts %s for router\"\n-                         \" %s because the addresses are invalid.\",\n-                         isb_pb->logical_port, isb_pb->transit_switch,\n-                         ic_lr->lr->name);\n-            continue;\n-        }\n-        lrp_name = get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port);\n-        lrp = get_lrp_by_lrp_name(ic, lrp_name);\n-        if (lrp) {\n-            route_table = smap_get_def(&lrp->options, \"route_table\", \"\");\n-            route_tag = smap_get_def(&lrp->options, \"ic-route-tag\", \"\");\n-        } else {\n-            route_table = \"\";\n-            route_tag = \"\";\n-        }\n-        build_ts_routes_to_adv(ic, ic_lr, routes_ad, &ts_port_addrs,\n-                               nb_global, route_table, route_tag, lrp);\n-        destroy_lport_addresses(&ts_port_addrs);\n-    }\n-}\n-\n-static void\n-delete_orphan_ic_routes(struct ic_input *ic,\n-                        const struct icsbrec_availability_zone *az)\n-{\n-    const struct icsbrec_route *isb_route, *isb_route_key =\n-        icsbrec_route_index_init_row(ic->icsbrec_route_by_az);\n-    icsbrec_route_index_set_availability_zone(isb_route_key, az);\n-\n-    const struct icnbrec_transit_switch *t_sw, *t_sw_key;\n-\n-    ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key,\n-                                  ic->icsbrec_route_by_az)\n-    {\n-        t_sw_key = icnbrec_transit_switch_index_init_row(\n-            ic->icnbrec_transit_switch_by_name);\n-        icnbrec_transit_switch_index_set_name(t_sw_key,\n-            isb_route->transit_switch);\n-        t_sw = icnbrec_transit_switch_index_find(\n-            ic->icnbrec_transit_switch_by_name, t_sw_key);\n-        icnbrec_transit_switch_index_destroy_row(t_sw_key);\n-\n-        if (!t_sw || !find_lrp_of_nexthop(ic, isb_route)) {\n-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n-            VLOG_INFO_RL(&rl, \"Deleting orphan ICDB:Route: %s->%s (%s, rtb:%s,\"\n-                         \" transit switch: %s)\", isb_route->ip_prefix,\n-                         isb_route->nexthop, isb_route->origin,\n-                         isb_route->route_table, isb_route->transit_switch);\n-            icsbrec_route_delete(isb_route);\n-        }\n-    }\n-    icsbrec_route_index_destroy_row(isb_route_key);\n-}\n-\n-static void\n-route_run(struct engine_context *ctx,\n-          struct ic_input *ic)\n-{\n-    if (!ctx->ovnisb_idl_txn || !ctx->ovnnb_idl_txn) {\n-        return;\n-    }\n-\n-    delete_orphan_ic_routes(ic, ic->runned_az);\n-\n-    struct hmap ic_lrs = HMAP_INITIALIZER(&ic_lrs);\n-    const struct icsbrec_port_binding *isb_pb;\n-    const struct icsbrec_port_binding *isb_pb_key =\n-        icsbrec_port_binding_index_init_row(ic->icsbrec_port_binding_by_az);\n-    icsbrec_port_binding_index_set_availability_zone(isb_pb_key,\n-        ic->runned_az);\n-\n-    /* Each port on TS maps to a logical router, which is stored in the\n-     * external_ids:router-id of the IC SB port_binding record.\n-     * Here we build info for interconnected Logical Router:\n-     * collect IC Port Binding to process routes sync later on. */\n-    ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,\n-                                         ic->icsbrec_port_binding_by_az)\n-    {\n-        if (ic_pb_get_type(isb_pb) == IC_ROUTER_PORT) {\n-            continue;\n-        }\n-        const struct nbrec_logical_switch_port *nb_lsp;\n-\n-        nb_lsp = get_lsp_by_ts_port_name(ic->nbrec_port_by_name,\n-                                         isb_pb->logical_port);\n-        if (!strcmp(nb_lsp->type, \"switch\")) {\n-            VLOG_DBG(\"IC-SB Port_Binding '%s' on ts '%s' corresponds to a \"\n-                     \"switch port, not considering for route collection.\",\n-                     isb_pb->logical_port, isb_pb->transit_switch);\n-            continue;\n-        }\n-\n-        const char *ts_lrp_name =\n-            get_lrp_name_by_ts_port_name(ic, isb_pb->logical_port);\n-        if (!ts_lrp_name) {\n-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n-            VLOG_WARN_RL(&rl, \"Route sync ignores port %s on ts %s because \"\n-                         \"logical router port is not found in NB. Deleting it\",\n-                         isb_pb->logical_port, isb_pb->transit_switch);\n-            icsbrec_port_binding_delete(isb_pb);\n-            continue;\n-        }\n-\n-        struct uuid lr_uuid;\n-        if (!smap_get_uuid(&isb_pb->external_ids, \"router-id\", &lr_uuid)) {\n-            VLOG_DBG(\"IC-SB Port_Binding %s doesn't have \"\n-                     \"external_ids:router-id set.\", isb_pb->logical_port);\n-            continue;\n-        }\n-\n-        const struct nbrec_logical_router *lr\n-            = nbrec_logical_router_table_get_for_uuid(\n-                ic->nbrec_logical_router_table, &lr_uuid);\n-        if (!lr) {\n-            continue;\n-        }\n-\n-        struct ic_router_info *ic_lr = ic_router_find(&ic_lrs, lr);\n-        if (!ic_lr) {\n-            ic_lr = xzalloc(sizeof *ic_lr);\n-            ic_lr->lr = lr;\n-            ic_lr->isb_pbs =\n-                VECTOR_EMPTY_INITIALIZER(const struct icsbrec_port_binding *);\n-            hmap_init(&ic_lr->routes_learned);\n-            hmap_insert(&ic_lrs, &ic_lr->node, uuid_hash(&lr->header_.uuid));\n-        }\n-        vector_push(&ic_lr->isb_pbs, &isb_pb);\n-    }\n-    icsbrec_port_binding_index_destroy_row(isb_pb_key);\n-\n-    struct ic_router_info *ic_lr;\n-    struct shash routes_ad_by_ts = SHASH_INITIALIZER(&routes_ad_by_ts);\n-    HMAP_FOR_EACH_SAFE (ic_lr, node, &ic_lrs) {\n-        collect_lr_routes(ic, ic_lr, &routes_ad_by_ts);\n-        sync_learned_routes(ctx, ic, ic_lr);\n-        vector_destroy(&ic_lr->isb_pbs);\n-        hmap_destroy(&ic_lr->routes_learned);\n-        hmap_remove(&ic_lrs, &ic_lr->node);\n-        free(ic_lr);\n-    }\n-    struct shash_node *node;\n-    SHASH_FOR_EACH (node, &routes_ad_by_ts) {\n-        advertise_routes(ctx, ic, ic->runned_az, node->name, node->data);\n-        hmap_destroy(node->data);\n-    }\n-    shash_destroy_free_data(&routes_ad_by_ts);\n-    hmap_destroy(&ic_lrs);\n-}\n-\n /*\n  * Data structures and functions related to\n  * synchronize health checks for load balancers\n@@ -2485,7 +1195,6 @@ ovn_db_run(struct ic_input *input_data,\n     gateway_run(eng_ctx, input_data);\n     ts_run(eng_ctx, input_data, ic_data->dp_tnlids, ic_data->isb_ts_dps);\n     tr_run(eng_ctx, input_data, ic_data->dp_tnlids, ic_data->isb_tr_dps);\n-    route_run(eng_ctx, input_data);\n     sync_service_monitor(eng_ctx, input_data);\n }\n \f\n@@ -2891,6 +1600,7 @@ main(int argc, char *argv[])\n     stopwatch_create(IC_OVN_DB_RUN_STOPWATCH_NAME, SW_MS);\n     stopwatch_create(OVN_IC_ENUM_DATAPATHS_RUN_STOPWATCH_NAME, SW_MS);\n     stopwatch_create(OVN_IC_PORT_BINDING_RUN_STOPWATCH_NAME, SW_MS);\n+    stopwatch_create(OVN_IC_ROUTE_RUN_STOPWATCH_NAME, SW_MS);\n \n     /* Initialize incremental processing engine for ovn-northd */\n     inc_proc_ic_init(&ovnnb_idl_loop, &ovnsb_idl_loop,\ndiff --git a/ic/ovn-ic.h b/ic/ovn-ic.h\nindex ff6317786..bc4a1e6e0 100644\n--- a/ic/ovn-ic.h\n+++ b/ic/ovn-ic.h\n@@ -22,7 +22,6 @@ struct ic_input {\n     /* Northbound table references */\n     const struct nbrec_logical_switch_table *nbrec_logical_switch_table;\n     const struct nbrec_logical_router_table *nbrec_logical_router_table;\n-    const struct nbrec_nb_global_table *nbrec_nb_global_table;\n \n     /* Southbound table references */\n     const struct sbrec_chassis_table *sbrec_chassis_table;\n@@ -54,10 +53,6 @@ struct ic_input {\n     struct ovsdb_idl_index *sbrec_service_monitor_by_ic_learned;\n     struct ovsdb_idl_index *sbrec_service_monitor_by_remote_type_logical_port;\n     struct ovsdb_idl_index *icnbrec_transit_switch_by_name;\n-    struct ovsdb_idl_index *icsbrec_port_binding_by_az;\n-    struct ovsdb_idl_index *icsbrec_route_by_az;\n-    struct ovsdb_idl_index *icsbrec_route_by_ts;\n-    struct ovsdb_idl_index *icsbrec_route_by_ts_az;\n     struct ovsdb_idl_index *icsbrec_service_monitor_by_source_az;\n     struct ovsdb_idl_index *icsbrec_service_monitor_by_target_az;\n     struct ovsdb_idl_index *icsbrec_service_monitor_by_target_az_logical_port;\n@@ -80,7 +75,8 @@ enum ic_datapath_type { IC_SWITCH, IC_ROUTER, IC_DATAPATH_MAX };\n enum ic_port_binding_type { IC_SWITCH_PORT, IC_ROUTER_PORT, IC_PORT_MAX };\n \n const struct nbrec_logical_router_port *\n-    get_lrp_by_lrp_name(struct ic_input *ic, const char *lrp_name);\n+get_lrp_by_lrp_name(struct ovsdb_idl_index *nbrec_lrp_by_name,\n+                    const char *lrp_name);\n const struct sbrec_port_binding * find_sb_pb_by_name(\n     struct ovsdb_idl_index *sbrec_port_binding_by_name, const char *name);\n const struct nbrec_logical_switch *\ndiff --git a/lib/stopwatch-names.h b/lib/stopwatch-names.h\nindex bf5da699c..c277d4056 100644\n--- a/lib/stopwatch-names.h\n+++ b/lib/stopwatch-names.h\n@@ -44,5 +44,6 @@\n #define IC_OVN_DB_RUN_STOPWATCH_NAME \"ovn_db_run\"\n #define OVN_IC_ENUM_DATAPATHS_RUN_STOPWATCH_NAME \"enum_datapaths_run\"\n #define OVN_IC_PORT_BINDING_RUN_STOPWATCH_NAME \"port_binding_run\"\n+#define OVN_IC_ROUTE_RUN_STOPWATCH_NAME \"route_run\"\n \n #endif\n",
    "prefixes": [
        "ovs-dev",
        "v0",
        "4/9"
    ]
}