Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.0/patches/2186653/?format=api
{ "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" ] }