{"id":2186654,"url":"http://patchwork.ozlabs.org/api/1.0/patches/2186654/?format=json","project":{"id":68,"url":"http://patchwork.ozlabs.org/api/1.0/projects/68/?format=json","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-4-guilherme.paulo@luizalabs.com>","date":"2026-01-20T11:49:42","name":"[ovs-dev,v0,3/9] ovn-ic: Add a new engine-node 'port-binding'.","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"9f967c39c775d1688546a702ade801d7c0a45b3d","submitter":{"id":90256,"url":"http://patchwork.ozlabs.org/api/1.0/people/90256/?format=json","name":"Paulo Guilherme Silva","email":"guilherme.paulo@luizalabs.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/ovn/patch/20260120114948.2289909-4-guilherme.paulo@luizalabs.com/mbox/","series":[{"id":489037,"url":"http://patchwork.ozlabs.org/api/1.0/series/489037/?format=json","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":"success","checks":"http://patchwork.ozlabs.org/api/patches/2186654/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=ivi7UlIS;\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=ivi7UlIS","smtp2.osuosl.org; dmarc=pass (p=quarantine dis=none)\n header.from=luizalabs.com","smtp2.osuosl.org;\n dkim=pass (1024-bit key) header.d=luizalabs.com header.i=@luizalabs.com\n header.a=rsa-sha256 header.s=google header.b=ivi7UlIS"],"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 4dwQd10CTgz1xsW\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 20 Jan 2026 22:50:45 +1100 (AEDT)","from localhost (localhost [127.0.0.1])\n\tby smtp4.osuosl.org (Postfix) with ESMTP id 364074EFCF;\n\tTue, 20 Jan 2026 11:50:43 +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 55O6zblBKhWm; Tue, 20 Jan 2026 11:50:35 +0000 (UTC)","from lists.linuxfoundation.org (lf-lists.osuosl.org\n [IPv6:2605:bc80:3010:104::8cd3:938])\n\tby smtp4.osuosl.org (Postfix) with ESMTPS id A69734EF9F;\n\tTue, 20 Jan 2026 11:50:34 +0000 (UTC)","from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id 8A7CCC02A6;\n\tTue, 20 Jan 2026 11:50:34 +0000 (UTC)","from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133])\n by lists.linuxfoundation.org (Postfix) with ESMTP id 3FE33C02A5\n for <dev@openvswitch.org>; Tue, 20 Jan 2026 11:50:33 +0000 (UTC)","from localhost (localhost [127.0.0.1])\n by smtp2.osuosl.org (Postfix) with ESMTP id F1C2A42F80\n for <dev@openvswitch.org>; Tue, 20 Jan 2026 11:50:27 +0000 (UTC)","from smtp2.osuosl.org ([127.0.0.1])\n by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id CAmF5vSqQ9Hc for <dev@openvswitch.org>;\n Tue, 20 Jan 2026 11:50:24 +0000 (UTC)","from mail-dy1-x1332.google.com (mail-dy1-x1332.google.com\n [IPv6:2607:f8b0:4864:20::1332])\n by smtp2.osuosl.org (Postfix) with ESMTPS id 0057742F67\n for <dev@openvswitch.org>; Tue, 20 Jan 2026 11:50:22 +0000 (UTC)","by mail-dy1-x1332.google.com with SMTP id\n 5a478bee46e88-2b6f85470b6so1528389eec.1\n for <dev@openvswitch.org>; Tue, 20 Jan 2026 03:50:22 -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.17\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 20 Jan 2026 03:50:19 -0800 (PST)"],"X-Virus-Scanned":["amavis at osuosl.org","amavis at osuosl.org"],"X-Comment":"SPF check N/A for local connections -\n client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN> ","DKIM-Filter":["OpenDKIM Filter v2.11.0 smtp4.osuosl.org A69734EF9F","OpenDKIM Filter v2.11.0 smtp2.osuosl.org 0057742F67"],"Received-SPF":"Pass (mailfrom) identity=mailfrom;\n client-ip=2607:f8b0:4864:20::1332; helo=mail-dy1-x1332.google.com;\n envelope-from=guilherme.paulo@luizalabs.com; receiver=<UNKNOWN>","DMARC-Filter":"OpenDMARC Filter v1.4.2 smtp2.osuosl.org 0057742F67","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=luizalabs.com; s=google; t=1768909822; x=1769514622; 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=XGMmMc/AlZNfoHMEMFRdWG7qdTI5IV6oFBqoh7Ylrqc=;\n b=ivi7UlIS6U5m3ngyaUgVYNPRWdu/bKwlwKWlKlEYBYrWJF7bMXpvcIgxwyPQk+IVhn\n GBMgynYG0nSB72mLksWnZuvW1StkjkTjCYZhh5YDD5zsUGGv0ML5qIBHIp06Gzu3m6cc\n dnLfQeNvwMWpNwJlvLTgN0pe6ZwOND38kkAbs=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20230601; t=1768909822; x=1769514622;\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=XGMmMc/AlZNfoHMEMFRdWG7qdTI5IV6oFBqoh7Ylrqc=;\n b=p3QdMFmpBQZAOLHmssHAko0rx43fhoCwP0RbburvoH9rZ3zcFAVDs1PIwLSYVmEskk\n V3aAMr6f2hjknX/pKMWFLwSbhHS32f2JHuA8ZCiPtl7HtP+xcbwYSOrGE7RrZMvkmUWs\n NyDMJBdZhMIgXCbd92acuZnZ/yXwKWGPm7I716A3gYTJHWOCs0WALPpxcK8NzJKN584i\n MSJ41BNUiDn67sRlPglbSUEuMYStF10u9StFZ+E0kJApjVCZfIAYJE/auN/sjPbJwblc\n d7pgW7dY/8yiRlUiZ5TFqq48/+NVnDr+HIMi+iu1tuQJ6j0G5JEdpP0eaQeacIBhtEJ6\n sN+A==","X-Gm-Message-State":"AOJu0Yx+i4FxhPxTzC3dN+fB+TvT9De3mGhGbVBCheikhOsPG9mE74CH\n 782dCuD1TB07N5EWKUialJuTPYtpzSKJ4Qz0kSMgtFCO/+HYvbcfwquif4xhDtE7FhOlEgE+BtX\n hOp4m5I2CYGxd6C12e/3vCx+R2cZWosLDtJ4dH1mbw9SWSyVZi1ScseVAI3Xt","X-Gm-Gg":"AZuq6aLdzxUTbjHzfHc5OwbK5fRrBdA9RWrbyvEkQA6owmgLDBnZ+J0BGsBduG8M3+H\n IRKrOQmeijPIV/mK9IETBfDXwebx9lnLwalS9AIFf3WeKd6KUkbAv2RMzkrIGxO0fDr4RABgKhD\n wXwu6h/4TCzmQc0L5MIB1UdTGqNWYxRsBsMZ3QOWWR2oEUtH6bEaGWtdmV2XMHUOzttVWIwL6aL\n iRRdQ7XV9FkVMEEHo0Zh4Y8sup0oAg91XAGetM6SLNz0Tk402VBMEEBX4m7J7/LoNSY4Ln9ljVp\n ctVYEdjEfNWXjy/Zt3GoBMVEArQokmm+AlBaMyDiXnJ7e7nDWv7Ad3psZ+BKoEBFmJwqJb/iPi2\n M6G89v6Hqm4FhcZMrlOzof1fb5xUwIVSrv/dJmrT/7bcBfzSGV/i04R/66f/FyTvWM5fVZoF0JC\n P8zWnd70HL3I6DJ/nwxVv3cBk/pMs=","X-Received":"by 2002:a05:7300:2315:b0:2ae:5ee8:b774 with SMTP id\n 5a478bee46e88-2b6fd7ed589mr1219649eec.9.1768909819491;\n Tue, 20 Jan 2026 03:50:19 -0800 (PST)","To":"dev@openvswitch.org","Date":"Tue, 20 Jan 2026 08:49:42 -0300","Message-Id":"<20260120114948.2289909-4-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 3/9] ovn-ic: Add a new engine-node\n 'port-binding'.","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 port-binding related data for ovn-ic\ndaemon which was earlier maintained by the ic\nengine node invoked the port_binding_run() function. The inputs to\nthis engine node are:\n   en_icnb_transit_switch;\n   en_icnb_transit_router;\n   en_icsb_port_binding;\n   en_nb_logical_switch;\n   en_sb_port_binding;\n   en_nb_logical_router;\n   en_sb_chassis;\nIn order to achieve this, we refactor in the following way:\n* Introduce port_binding_init() which initializes this data.\n* Introduce port_binding_destroy() which clears this data for a new iteration.\n* Introduce port_binding_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            |   8 -\n ic/en-port-binding.c  | 853 ++++++++++++++++++++++++++++++++++++++++++\n ic/en-port-binding.h  |  36 ++\n ic/inc-proc-ic.c      |  13 +-\n ic/ovn-ic.c           | 701 ++--------------------------------\n ic/ovn-ic.h           |  25 +-\n lib/stopwatch-names.h |   1 +\n 8 files changed, 956 insertions(+), 683 deletions(-)\n create mode 100644 ic/en-port-binding.c\n create mode 100644 ic/en-port-binding.h","diff":"diff --git a/ic/automake.mk b/ic/automake.mk\nindex 2766483b7..91783df0f 100644\n--- a/ic/automake.mk\n+++ b/ic/automake.mk\n@@ -6,6 +6,8 @@ ic_ovn_ic_SOURCES = ic/ovn-ic.c \\\n \tic/en-ic.h \\\n \tic/en-enum-datapaths.c \\\n \tic/en-enum-datapaths.h \\\n+\tic/en-port-binding.c \\\n+\tic/en-port-binding.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 16b0e12bd..e7c7ab71b 100644\n--- a/ic/en-ic.c\n+++ b/ic/en-ic.c\n@@ -116,14 +116,6 @@ ic_get_input_data(struct engine_node *node,\n         engine_ovsdb_node_get_index(\n             engine_get_input(\"ICSB_port_binding\", node),\n             \"icsbrec_port_binding_by_az\");\n-    input_data->icsbrec_port_binding_by_ts =\n-        engine_ovsdb_node_get_index(\n-            engine_get_input(\"ICSB_port_binding\", node),\n-            \"icsbrec_port_binding_by_ts\");\n-    input_data->icsbrec_port_binding_by_ts_az =\n-        engine_ovsdb_node_get_index(\n-            engine_get_input(\"ICSB_port_binding\", node),\n-            \"icsbrec_port_binding_by_ts_az\");\n     input_data->icsbrec_route_by_az =\n         engine_ovsdb_node_get_index(\n             engine_get_input(\"ICSB_route\", node),\ndiff --git a/ic/en-port-binding.c b/ic/en-port-binding.c\nnew file mode 100644\nindex 000000000..ee9df2830\n--- /dev/null\n+++ b/ic/en-port-binding.c\n@@ -0,0 +1,853 @@\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+\n+/* OVS includes. */\n+#include \"openvswitch/vlog.h\"\n+\n+/* OVN includes. */\n+#include \"ovn-ic.h\"\n+#include \"en-port-binding.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-sb-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_port_binding);\n+COVERAGE_DEFINE(port_binding_run);\n+\n+static void\n+port_binding_run(const struct engine_context *eng_ctx,\n+                 struct pb_input *pb_input,\n+                 struct ed_type_port_binding *pb_data,\n+                 const struct icnbrec_transit_switch_table *icnb_ts_table,\n+                 const struct icnbrec_transit_router_table *icnb_tr_table);\n+static void port_binding_init(struct ed_type_port_binding *data);\n+static void port_binding_destroy(struct ed_type_port_binding *data);\n+static void port_binding_clear(struct ed_type_port_binding *data);\n+static void port_binding_get_input_data(struct engine_node *node,\n+                                        struct pb_input *input_data);\n+static const struct nbrec_logical_router *\n+    find_tr_in_nb(struct pb_input *pb, char *tr_name);\n+static const struct sbrec_port_binding *\n+    find_peer_port(struct pb_input *pb,\n+                   const struct sbrec_port_binding *sb_pb);\n+static const struct sbrec_port_binding *\n+    find_crp_from_lrp(struct pb_input *pb,\n+                      const struct sbrec_port_binding *lrp_pb);\n+static const struct sbrec_port_binding *\n+    find_crp_for_sb_pb(struct pb_input *pb,\n+                       const struct sbrec_port_binding *sb_pb);\n+static const char *\n+    get_lp_address_for_sb_pb(struct pb_input *pb,\n+                             const struct sbrec_port_binding *sb_pb);\n+static const struct sbrec_chassis *\n+    find_sb_chassis(struct pb_input *pb, const char *name);\n+static void sync_lsp_tnl_key(const struct nbrec_logical_switch_port *lsp,\n+                             int64_t isb_tnl_key);\n+static inline void\n+    sync_lrp_tnl_key(const struct nbrec_logical_router_port *lrp,\n+                     int64_t isb_tnl_key);\n+static bool\n+    get_router_uuid_by_sb_pb(struct pb_input *pb,\n+                             const struct sbrec_port_binding *sb_pb,\n+                             struct uuid *router_uuid);\n+static void\n+    update_isb_pb_external_ids(struct pb_input *pb,\n+                               const struct sbrec_port_binding *sb_pb,\n+                               const struct icsbrec_port_binding *isb_pb);\n+static void\n+    sync_local_port(struct pb_input *pb,\n+                    const struct icsbrec_port_binding *isb_pb,\n+                    const struct sbrec_port_binding *sb_pb,\n+                    const struct nbrec_logical_switch_port *lsp);\n+static void\n+    sync_remote_port(struct pb_input *pb,\n+                     const struct icsbrec_port_binding *isb_pb,\n+                     const struct nbrec_logical_switch_port *lsp,\n+                     const struct sbrec_port_binding *sb_pb);\n+static void\n+    sync_router_port(const struct icsbrec_port_binding *isb_pb,\n+                     const struct icnbrec_transit_router_port *trp,\n+                     const struct nbrec_logical_router_port *lrp);\n+static void\n+    create_nb_lsp(const struct engine_context *ctx,\n+                  const struct icsbrec_port_binding *isb_pb,\n+                  const struct nbrec_logical_switch *ls);\n+static uint32_t allocate_port_key(struct hmap *pb_tnlids);\n+static const struct icsbrec_port_binding *\n+    create_isb_pb(const struct engine_context *ctx, const char *logical_port,\n+                  const struct icsbrec_availability_zone *az,\n+                  const char *ts_name, const struct uuid *nb_ic_uuid,\n+                  const char *type, struct hmap *pb_tnlids);\n+static bool trp_is_remote(struct pb_input *pb, const char *chassis_name);\n+static struct nbrec_logical_router_port *\n+    lrp_create(const struct engine_context *ctx,\n+               const struct nbrec_logical_router *lr,\n+               const struct icnbrec_transit_router_port *trp);\n+static void\n+    sync_ts_isb_pb(struct pb_input *pb, const struct sbrec_port_binding *sb_pb,\n+                   const struct icsbrec_port_binding *isb_pb);\n+static const struct sbrec_port_binding *\n+    find_lsp_in_sb(struct pb_input *pb,\n+                   const struct nbrec_logical_switch_port *lsp);\n+\n+static void\n+port_binding_get_input_data(struct engine_node *node,\n+                            struct pb_input *input_data)\n+{\n+    /* Indexes */\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_port_binding_by_ts =\n+        engine_ovsdb_node_get_index(\n+            engine_get_input(\"ICSB_port_binding\", node),\n+            \"icsbrec_port_binding_by_ts\");\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->sbrec_port_binding_by_name =\n+        engine_ovsdb_node_get_index(\n+            engine_get_input(\"SB_port_binding\", node),\n+            \"sbrec_port_binding_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_lr_by_name =\n+        engine_ovsdb_node_get_index(\n+            engine_get_input(\"NB_logical_router\", node),\n+            \"nbrec_lr_by_name\");\n+    input_data->sbrec_chassis_by_name =\n+        engine_ovsdb_node_get_index(\n+            engine_get_input(\"SB_chassis\", node),\n+            \"sbrec_chassis_by_name\");\n+}\n+\n+enum engine_node_state\n+en_port_binding_run(struct engine_node *node, void *data)\n+{\n+    const struct engine_context *eng_ctx = engine_get_context();\n+    struct ed_type_port_binding *pb_data = data;\n+    struct pb_input pb_input;\n+\n+    port_binding_clear(pb_data);\n+\n+    const struct icnbrec_transit_switch_table *icnb_ts_table =\n+        EN_OVSDB_GET(engine_get_input(\"ICNB_transit_switch\", node));\n+    const struct icnbrec_transit_router_table *icnb_tr_table =\n+        EN_OVSDB_GET(engine_get_input(\"ICNB_transit_router\", node));\n+\n+    port_binding_get_input_data(node, &pb_input);\n+    pb_input.runned_az = eng_ctx->client_ctx;\n+\n+    COVERAGE_INC(port_binding_run);\n+    stopwatch_start(OVN_IC_PORT_BINDING_RUN_STOPWATCH_NAME, time_usec());\n+    port_binding_run(eng_ctx, &pb_input, pb_data, icnb_ts_table,\n+                     icnb_tr_table);\n+    stopwatch_stop(OVN_IC_PORT_BINDING_RUN_STOPWATCH_NAME, time_usec());\n+\n+    return EN_UPDATED;\n+}\n+\n+void *\n+en_port_binding_init(struct engine_node *node OVS_UNUSED,\n+                     struct engine_arg *arg OVS_UNUSED)\n+{\n+    struct ed_type_port_binding *data = xzalloc(sizeof *data);\n+    port_binding_init(data);\n+    return data;\n+}\n+\n+void\n+en_port_binding_cleanup(void *data)\n+{\n+    port_binding_destroy(data);\n+}\n+\n+static void\n+port_binding_init(struct ed_type_port_binding *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+port_binding_destroy(struct ed_type_port_binding *data)\n+{\n+    port_binding_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+port_binding_clear(struct ed_type_port_binding *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+port_binding_run(const struct engine_context *eng_ctx,\n+                 struct pb_input *pb_input,\n+                 struct ed_type_port_binding *pb_data,\n+                 const struct icnbrec_transit_switch_table *icnb_ts_table,\n+                 const struct icnbrec_transit_router_table *icnb_tr_table)\n+{\n+    if (!eng_ctx->ovnisb_idl_txn || !eng_ctx->ovnnb_idl_txn\n+        || !eng_ctx->ovnsb_idl_txn) {\n+        return;\n+    }\n+\n+    struct shash_node *node;\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+            pb_input->icsbrec_port_binding_by_az);\n+    icsbrec_port_binding_index_set_availability_zone(isb_pb_key,\n+                                                     pb_input->runned_az);\n+\n+    ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,\n+        pb_input->icsbrec_port_binding_by_az) {\n+        ic_pb_get_type(isb_pb) != IC_ROUTER_PORT\n+            ? shash_add(&pb_data->switch_all_local_pbs, isb_pb->logical_port,\n+                        isb_pb)\n+            : shash_add(&pb_data->router_all_local_pbs, isb_pb->logical_port,\n+                        isb_pb);\n+\n+        ovn_add_tnlid(&pb_data->pb_tnlids, isb_pb->tunnel_key);\n+    }\n+    icsbrec_port_binding_index_destroy_row(isb_pb_key);\n+\n+    const struct sbrec_port_binding *sb_pb;\n+    const struct icnbrec_transit_switch *ts;\n+    ICNBREC_TRANSIT_SWITCH_TABLE_FOR_EACH (ts, icnb_ts_table) {\n+        const struct nbrec_logical_switch *ls =\n+            find_ts_in_nb(pb_input->nbrec_ls_by_name,\n+                          ts->name);\n+        if (!ls) {\n+            VLOG_DBG(\"Transit switch %s not found in NB.\", ts->name);\n+            continue;\n+        }\n+        struct shash local_pbs = SHASH_INITIALIZER(&local_pbs);\n+        struct shash remote_pbs = SHASH_INITIALIZER(&remote_pbs);\n+\n+        isb_pb_key = icsbrec_port_binding_index_init_row(\n+            pb_input->icsbrec_port_binding_by_ts);\n+        icsbrec_port_binding_index_set_transit_switch(isb_pb_key, ts->name);\n+\n+        ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,\n+            pb_input->icsbrec_port_binding_by_ts) {\n+            if (isb_pb->availability_zone == pb_input->runned_az) {\n+                shash_add(&local_pbs, isb_pb->logical_port, isb_pb);\n+                shash_find_and_delete(&pb_data->switch_all_local_pbs,\n+                                      isb_pb->logical_port);\n+            } else {\n+                shash_add(&remote_pbs, isb_pb->logical_port, isb_pb);\n+            }\n+        }\n+        icsbrec_port_binding_index_destroy_row(isb_pb_key);\n+\n+        const struct nbrec_logical_switch_port *lsp;\n+        for (int i = 0; i < ls->n_ports; i++) {\n+            lsp = ls->ports[i];\n+\n+            if (!strcmp(lsp->type, \"router\")\n+                || !strcmp(lsp->type, \"switch\")) {\n+                /* The port is local. */\n+                sb_pb = find_lsp_in_sb(pb_input, lsp);\n+                if (!sb_pb) {\n+                    continue;\n+                }\n+                isb_pb = shash_find_and_delete(&local_pbs, lsp->name);\n+                if (!isb_pb) {\n+                    isb_pb = create_isb_pb(\n+                        eng_ctx, sb_pb->logical_port, pb_input->runned_az,\n+                        ts->name, &ts->header_.uuid, \"transit-switch-port\",\n+                        &pb_data->pb_tnlids);\n+                    sync_ts_isb_pb(pb_input, sb_pb, isb_pb);\n+                } else {\n+                    sync_local_port(pb_input, isb_pb, sb_pb, lsp);\n+                }\n+\n+                if (isb_pb->type) {\n+                    icsbrec_port_binding_set_type(isb_pb,\n+                                                  \"transit-switch-port\");\n+                }\n+\n+                if (isb_pb->nb_ic_uuid) {\n+                    icsbrec_port_binding_set_nb_ic_uuid(isb_pb,\n+                                                        &ts->header_.uuid, 1);\n+                }\n+            } else if (!strcmp(lsp->type, \"remote\")) {\n+                /* The port is remote. */\n+                isb_pb = shash_find_and_delete(&remote_pbs, lsp->name);\n+                if (!isb_pb) {\n+                    nbrec_logical_switch_update_ports_delvalue(ls, lsp);\n+                } else {\n+                    sb_pb = find_lsp_in_sb(pb_input, lsp);\n+                    if (!sb_pb) {\n+                        continue;\n+                    }\n+                    sync_remote_port(pb_input, isb_pb, lsp, sb_pb);\n+                }\n+            } else {\n+                VLOG_DBG(\"Ignore lsp %s on ts %s with type %s.\",\n+                         lsp->name, ts->name, lsp->type);\n+            }\n+        }\n+\n+        /* Delete extra port-binding from ISB */\n+        SHASH_FOR_EACH (node, &local_pbs) {\n+            icsbrec_port_binding_delete(node->data);\n+        }\n+\n+        /* Create lsp in NB for remote ports */\n+        SHASH_FOR_EACH (node, &remote_pbs) {\n+            create_nb_lsp(eng_ctx, node->data, ls);\n+        }\n+\n+        shash_destroy(&local_pbs);\n+        shash_destroy(&remote_pbs);\n+    }\n+\n+    SHASH_FOR_EACH (node, &pb_data->switch_all_local_pbs) {\n+        icsbrec_port_binding_delete(node->data);\n+    }\n+\n+    const struct icnbrec_transit_router *tr;\n+    ICNBREC_TRANSIT_ROUTER_TABLE_FOR_EACH (tr, icnb_tr_table) {\n+        const struct nbrec_logical_router *lr = find_tr_in_nb(pb_input,\n+                                                              tr->name);\n+        if (!lr) {\n+            VLOG_DBG(\"Transit router %s not found in NB.\", tr->name);\n+            continue;\n+        }\n+\n+        struct shash nb_ports = SHASH_INITIALIZER(&nb_ports);\n+        struct shash local_pbs = SHASH_INITIALIZER(&local_pbs);\n+        struct shash remote_pbs = SHASH_INITIALIZER(&remote_pbs);\n+\n+        for (size_t i = 0; i < lr->n_ports; i++) {\n+            const struct nbrec_logical_router_port *lrp = lr->ports[i];\n+            if (smap_get_def(&lrp->options, \"interconn-tr\", NULL)) {\n+                shash_add(&nb_ports, lrp->name, lrp);\n+            }\n+        }\n+\n+        isb_pb_key = icsbrec_port_binding_index_init_row(\n+            pb_input->icsbrec_port_binding_by_ts);\n+        icsbrec_port_binding_index_set_transit_switch(isb_pb_key, tr->name);\n+\n+        ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,\n+            pb_input->icsbrec_port_binding_by_ts) {\n+            if (isb_pb->availability_zone == pb_input->runned_az) {\n+                shash_add(&local_pbs, isb_pb->logical_port, isb_pb);\n+                shash_find_and_delete(&pb_data->router_all_local_pbs,\n+                                      isb_pb->logical_port);\n+            } else {\n+                shash_add(&remote_pbs, isb_pb->logical_port, isb_pb);\n+            }\n+        }\n+        icsbrec_port_binding_index_destroy_row(isb_pb_key);\n+\n+        for (size_t i = 0; i < tr->n_ports; i++) {\n+            const struct icnbrec_transit_router_port *trp = tr->ports[i];\n+\n+            if (trp_is_remote(pb_input, trp->chassis)) {\n+                isb_pb = shash_find_and_delete(&remote_pbs, trp->name);\n+            } else {\n+                isb_pb = shash_find_and_delete(&local_pbs, trp->name);\n+                if (!isb_pb) {\n+                    isb_pb = create_isb_pb(eng_ctx, trp->name,\n+                                           pb_input->runned_az, tr->name,\n+                                           &tr->header_.uuid,\n+                                           \"transit-router-port\",\n+                                           &pb_data->pb_tnlids);\n+                    icsbrec_port_binding_set_address(isb_pb, trp->mac);\n+                }\n+            }\n+\n+            /* Don't allow remote ports to create NB LRP until ICSB entry is\n+             * created in the appropriate AZ. */\n+            if (isb_pb) {\n+                const struct nbrec_logical_router_port *lrp =\n+                    shash_find_and_delete(&nb_ports, trp->name);\n+                if (!lrp) {\n+                    lrp = lrp_create(eng_ctx, lr, trp);\n+                }\n+\n+                sync_router_port(isb_pb, trp, lrp);\n+            }\n+        }\n+\n+        SHASH_FOR_EACH (node, &nb_ports) {\n+            nbrec_logical_router_port_delete(node->data);\n+            nbrec_logical_router_update_ports_delvalue(lr, node->data);\n+        }\n+\n+        shash_destroy(&nb_ports);\n+        shash_destroy(&local_pbs);\n+        shash_destroy(&remote_pbs);\n+    }\n+\n+    SHASH_FOR_EACH (node, &pb_data->router_all_local_pbs) {\n+        icsbrec_port_binding_delete(node->data);\n+    }\n+}\n+\n+static const struct nbrec_logical_router *\n+find_tr_in_nb(struct pb_input *pb, char *tr_name)\n+{\n+    const struct nbrec_logical_router *key =\n+        nbrec_logical_router_index_init_row(pb->nbrec_lr_by_name);\n+    nbrec_logical_router_index_set_name(key, tr_name);\n+\n+    const struct nbrec_logical_router *lr;\n+    bool found = false;\n+    NBREC_LOGICAL_ROUTER_FOR_EACH_EQUAL (lr, key, pb->nbrec_lr_by_name) {\n+        if (smap_get(&lr->options, \"interconn-tr\")) {\n+            found = true;\n+            break;\n+        }\n+    }\n+\n+    nbrec_logical_router_index_destroy_row(key);\n+    if (found) {\n+        return lr;\n+    }\n+\n+    return NULL;\n+}\n+\n+static const struct sbrec_port_binding *\n+find_peer_port(struct pb_input *pb,\n+               const struct sbrec_port_binding *sb_pb)\n+{\n+    const char *peer_name = smap_get(&sb_pb->options, \"peer\");\n+    if (!peer_name) {\n+        return NULL;\n+    }\n+\n+    return find_sb_pb_by_name(pb->sbrec_port_binding_by_name, peer_name);\n+}\n+\n+static const struct sbrec_port_binding *\n+find_crp_from_lrp(struct pb_input *pb,\n+                  const struct sbrec_port_binding *lrp_pb)\n+{\n+    char *crp_name = ovn_chassis_redirect_name(lrp_pb->logical_port);\n+\n+    const struct sbrec_port_binding *sb_pb =\n+        find_sb_pb_by_name(pb->sbrec_port_binding_by_name, crp_name);\n+\n+    free(crp_name);\n+    return sb_pb;\n+}\n+\n+static const struct sbrec_port_binding *\n+find_crp_for_sb_pb(struct pb_input *pb,\n+                   const struct sbrec_port_binding *sb_pb)\n+{\n+    const struct sbrec_port_binding *peer = find_peer_port(pb, sb_pb);\n+    if (!peer) {\n+        return NULL;\n+    }\n+\n+    return find_crp_from_lrp(pb, peer);\n+}\n+\n+static const char *\n+get_lp_address_for_sb_pb(struct pb_input *pb,\n+                         const struct sbrec_port_binding *sb_pb)\n+{\n+    const struct nbrec_logical_switch_port *nb_lsp;\n+\n+    nb_lsp = get_lsp_by_ts_port_name(pb->nbrec_port_by_name,\n+                                     sb_pb->logical_port);\n+    if (!strcmp(nb_lsp->type, \"switch\")) {\n+        /* Switches always have implicit \"unknown\" address, and IC-SB port\n+         * binding can only have one address specified. */\n+        return \"unknown\";\n+    }\n+\n+    const struct sbrec_port_binding *peer = find_peer_port(pb, sb_pb);\n+    if (!peer) {\n+        return NULL;\n+    }\n+\n+    return peer->n_mac ? *peer->mac : NULL;\n+}\n+\n+static const struct sbrec_chassis *\n+find_sb_chassis(struct pb_input *pb, const char *name)\n+{\n+    const struct sbrec_chassis *key =\n+        sbrec_chassis_index_init_row(pb->sbrec_chassis_by_name);\n+    sbrec_chassis_index_set_name(key, name);\n+\n+    const struct sbrec_chassis *chassis =\n+        sbrec_chassis_index_find(pb->sbrec_chassis_by_name, key);\n+    sbrec_chassis_index_destroy_row(key);\n+\n+    return chassis;\n+}\n+\n+static void\n+sync_lsp_tnl_key(const struct nbrec_logical_switch_port *lsp,\n+                 int64_t isb_tnl_key)\n+{\n+    int64_t tnl_key = smap_get_int(&lsp->options, \"requested-tnl-key\", 0);\n+    if (tnl_key != isb_tnl_key) {\n+        VLOG_DBG(\"Set options:requested-tnl-key %\"PRId64\n+                 \" for lsp %s in NB.\", isb_tnl_key, lsp->name);\n+        char *tnl_key_str = xasprintf(\"%\"PRId64, isb_tnl_key);\n+        nbrec_logical_switch_port_update_options_setkey(lsp,\n+                                                        \"requested-tnl-key\",\n+                                                        tnl_key_str);\n+        free(tnl_key_str);\n+    }\n+}\n+\n+static inline void\n+sync_lrp_tnl_key(const struct nbrec_logical_router_port *lrp,\n+                 int64_t isb_tnl_key)\n+{\n+    int64_t tnl_key = smap_get_int(&lrp->options, \"requested-tnl-key\", 0);\n+    if (tnl_key != isb_tnl_key) {\n+        VLOG_DBG(\"Set options:requested-tnl-key %\" PRId64 \" for lrp %s in NB.\",\n+                 isb_tnl_key, lrp->name);\n+        char *tnl_key_str = xasprintf(\"%\"PRId64, isb_tnl_key);\n+        nbrec_logical_router_port_update_options_setkey(\n+            lrp, \"requested-tnl-key\", tnl_key_str);\n+        free(tnl_key_str);\n+    }\n+}\n+\n+static bool\n+get_router_uuid_by_sb_pb(struct pb_input *pb,\n+                         const struct sbrec_port_binding *sb_pb,\n+                         struct uuid *router_uuid)\n+{\n+    const struct sbrec_port_binding *router_pb = find_peer_port(pb, sb_pb);\n+    if (!router_pb || !router_pb->datapath) {\n+        return NULL;\n+    }\n+\n+    return datapath_get_nb_uuid(router_pb->datapath, router_uuid);\n+}\n+\n+static void\n+update_isb_pb_external_ids(struct pb_input *pb,\n+                           const struct sbrec_port_binding *sb_pb,\n+                           const struct icsbrec_port_binding *isb_pb)\n+{\n+    struct uuid lr_uuid;\n+    if (!get_router_uuid_by_sb_pb(pb, sb_pb, &lr_uuid)) {\n+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n+        VLOG_WARN_RL(&rl, \"Can't get router uuid for transit switch port %s.\",\n+                     isb_pb->logical_port);\n+        return;\n+    }\n+\n+    struct uuid current_lr_uuid;\n+    if (smap_get_uuid(&isb_pb->external_ids, \"router-id\", &current_lr_uuid) &&\n+        uuid_equals(&lr_uuid, &current_lr_uuid)) {\n+        return;\n+    }\n+\n+    char *uuid_s = xasprintf(UUID_FMT, UUID_ARGS(&lr_uuid));\n+    icsbrec_port_binding_update_external_ids_setkey(isb_pb, \"router-id\",\n+                                                    uuid_s);\n+    free(uuid_s);\n+}\n+\n+/* For each local port:\n+ *   - Sync from NB to ISB.\n+ *   - Sync gateway from SB to ISB.\n+ *   - Sync tunnel key from ISB to NB.\n+ */\n+static void\n+sync_local_port(struct pb_input *pb,\n+                const struct icsbrec_port_binding *isb_pb,\n+                const struct sbrec_port_binding *sb_pb,\n+                const struct nbrec_logical_switch_port *lsp)\n+{\n+    /* Sync address from NB to ISB */\n+    const char *address = get_lp_address_for_sb_pb(pb, sb_pb);\n+    if (!address) {\n+        VLOG_DBG(\"Can't get router/switch port address for logical\"\n+                 \" switch port %s\", sb_pb->logical_port);\n+        if (isb_pb->address[0]) {\n+            icsbrec_port_binding_set_address(isb_pb, \"\");\n+        }\n+    } else {\n+        if (strcmp(address, isb_pb->address)) {\n+            icsbrec_port_binding_set_address(isb_pb, address);\n+        }\n+    }\n+\n+    /* Sync gateway from SB to ISB */\n+    const struct sbrec_port_binding *crp = find_crp_for_sb_pb(pb, sb_pb);\n+    if (crp && crp->chassis) {\n+        if (strcmp(crp->chassis->name, isb_pb->gateway)) {\n+            icsbrec_port_binding_set_gateway(isb_pb, crp->chassis->name);\n+        }\n+    } else if (!strcmp(lsp->type, \"switch\") && sb_pb->chassis) {\n+        if (strcmp(sb_pb->chassis->name, isb_pb->gateway)) {\n+            icsbrec_port_binding_set_gateway(isb_pb, sb_pb->chassis->name);\n+        }\n+    } else {\n+        if (isb_pb->gateway[0]) {\n+            icsbrec_port_binding_set_gateway(isb_pb, \"\");\n+        }\n+    }\n+\n+    /* Sync external_ids:router-id to ISB */\n+    update_isb_pb_external_ids(pb, sb_pb, isb_pb);\n+\n+    /* Sync back tunnel key from ISB to NB */\n+    sync_lsp_tnl_key(lsp, isb_pb->tunnel_key);\n+}\n+\n+/* For each remote port:\n+ *   - Sync from ISB to NB\n+ *   - Sync gateway from ISB to SB\n+ */\n+static void\n+sync_remote_port(struct pb_input *pb,\n+                 const struct icsbrec_port_binding *isb_pb,\n+                 const struct nbrec_logical_switch_port *lsp,\n+                 const struct sbrec_port_binding *sb_pb)\n+{\n+    /* Sync address from ISB to NB */\n+    if (isb_pb->address[0]) {\n+        if (lsp->n_addresses != 1 ||\n+            strcmp(isb_pb->address, lsp->addresses[0])) {\n+            nbrec_logical_switch_port_set_addresses(\n+                lsp, (const char **)&isb_pb->address, 1);\n+        }\n+    } else {\n+        if (lsp->n_addresses != 0) {\n+            nbrec_logical_switch_port_set_addresses(lsp, NULL, 0);\n+        }\n+    }\n+\n+    /* Sync tunnel key from ISB to NB */\n+    sync_lsp_tnl_key(lsp, isb_pb->tunnel_key);\n+\n+    /* Skip port binding if it is already requested by the CMS. */\n+    if (smap_get(&lsp->options, \"requested-chassis\")) {\n+        return;\n+    }\n+\n+    /* Sync gateway from ISB to SB */\n+    if (isb_pb->gateway[0]) {\n+        if (!sb_pb->chassis || strcmp(sb_pb->chassis->name, isb_pb->gateway)) {\n+            const struct sbrec_chassis *chassis =\n+                find_sb_chassis(pb, isb_pb->gateway);\n+            if (!chassis) {\n+                VLOG_DBG(\"Chassis %s is not found in SB, syncing from ISB \"\n+                         \"to SB skipped for logical port %s.\",\n+                         isb_pb->gateway, lsp->name);\n+                return;\n+            }\n+            sbrec_port_binding_set_chassis(sb_pb, chassis);\n+        }\n+    } else {\n+        if (sb_pb->chassis) {\n+            sbrec_port_binding_set_chassis(sb_pb, NULL);\n+        }\n+    }\n+}\n+\n+/* For each remote port:\n+ *   - Sync from ISB to NB\n+ */\n+static void\n+sync_router_port(const struct icsbrec_port_binding *isb_pb,\n+                 const struct icnbrec_transit_router_port *trp,\n+                 const struct nbrec_logical_router_port *lrp)\n+{\n+    /* Sync from ICNB to NB */\n+    if (trp->chassis[0]) {\n+        const char *chassis_name =\n+            smap_get_def(&lrp->options, \"requested-chassis\", \"\");\n+        if (strcmp(trp->chassis, chassis_name)) {\n+            nbrec_logical_router_port_update_options_setkey(\n+                lrp, \"requested-chassis\", trp->chassis);\n+        }\n+    } else {\n+        nbrec_logical_router_port_update_options_delkey(\n+            lrp, \"requested-chassis\");\n+    }\n+\n+    if (strcmp(trp->mac, lrp->mac)) {\n+        nbrec_logical_router_port_set_mac(lrp, trp->mac);\n+    }\n+\n+    bool sync_networks = false;\n+    if (trp->n_networks != lrp->n_networks) {\n+        sync_networks = true;\n+    } else {\n+        for (size_t i = 0; i < trp->n_networks; i++) {\n+            if (strcmp(trp->networks[i], lrp->networks[i])) {\n+                sync_networks |= true;\n+                break;\n+            }\n+        }\n+    }\n+\n+    if (sync_networks) {\n+        nbrec_logical_router_port_set_networks(\n+            lrp, (const char **) trp->networks, trp->n_networks);\n+    }\n+\n+    /* Sync tunnel key from ISB to NB */\n+    sync_lrp_tnl_key(lrp, isb_pb->tunnel_key);\n+}\n+\n+static void\n+create_nb_lsp(const struct engine_context *ctx,\n+              const struct icsbrec_port_binding *isb_pb,\n+              const struct nbrec_logical_switch *ls)\n+{\n+    const struct nbrec_logical_switch_port *lsp =\n+        nbrec_logical_switch_port_insert(ctx->ovnnb_idl_txn);\n+    nbrec_logical_switch_port_set_name(lsp, isb_pb->logical_port);\n+    nbrec_logical_switch_port_set_type(lsp, \"remote\");\n+\n+    bool up = true;\n+    nbrec_logical_switch_port_set_up(lsp, &up, 1);\n+\n+    if (isb_pb->address[0]) {\n+        nbrec_logical_switch_port_set_addresses(\n+            lsp, (const char **)&isb_pb->address, 1);\n+    }\n+    sync_lsp_tnl_key(lsp, isb_pb->tunnel_key);\n+    nbrec_logical_switch_update_ports_addvalue(ls, lsp);\n+}\n+\n+static uint32_t\n+allocate_port_key(struct hmap *pb_tnlids)\n+{\n+    static uint32_t hint;\n+    return ovn_allocate_tnlid(pb_tnlids, \"transit port\",\n+                              1, (1u << 15) - 1, &hint);\n+}\n+\n+static const struct icsbrec_port_binding *\n+create_isb_pb(const struct engine_context *ctx, const char *logical_port,\n+              const struct icsbrec_availability_zone *az, const char *ts_name,\n+              const struct uuid *nb_ic_uuid, const char *type,\n+              struct hmap *pb_tnlids)\n+{\n+    uint32_t pb_tnl_key = allocate_port_key(pb_tnlids);\n+    if (!pb_tnl_key) {\n+        return NULL;\n+    }\n+\n+    const struct icsbrec_port_binding *isb_pb =\n+        icsbrec_port_binding_insert(ctx->ovnisb_idl_txn);\n+    icsbrec_port_binding_set_availability_zone(isb_pb, az);\n+    icsbrec_port_binding_set_transit_switch(isb_pb, ts_name);\n+    icsbrec_port_binding_set_logical_port(isb_pb, logical_port);\n+    icsbrec_port_binding_set_tunnel_key(isb_pb, pb_tnl_key);\n+    icsbrec_port_binding_set_nb_ic_uuid(isb_pb, nb_ic_uuid, 1);\n+    icsbrec_port_binding_set_type(isb_pb, type);\n+    return isb_pb;\n+}\n+\n+static bool\n+trp_is_remote(struct pb_input *pb, const char *chassis_name)\n+{\n+    if (chassis_name) {\n+        const struct sbrec_chassis *chassis =\n+            find_sb_chassis(pb, chassis_name);\n+        if (chassis) {\n+            return smap_get_bool(&chassis->other_config, \"is-remote\", false);\n+        } else {\n+            return true;\n+        }\n+    }\n+\n+    return false;\n+}\n+\n+static struct nbrec_logical_router_port *\n+lrp_create(const struct engine_context *ctx,\n+           const struct nbrec_logical_router *lr,\n+           const struct icnbrec_transit_router_port *trp)\n+{\n+    struct nbrec_logical_router_port *lrp =\n+        nbrec_logical_router_port_insert(ctx->ovnnb_idl_txn);\n+    nbrec_logical_router_port_set_name(lrp, trp->name);\n+\n+    nbrec_logical_router_port_update_options_setkey(lrp, \"interconn-tr\",\n+                                                    trp->name);\n+    nbrec_logical_router_update_ports_addvalue(lr, lrp);\n+    return lrp;\n+}\n+\n+static void\n+sync_ts_isb_pb(struct pb_input *pb, const struct sbrec_port_binding *sb_pb,\n+               const struct icsbrec_port_binding *isb_pb)\n+{\n+    const char *address = get_lp_address_for_sb_pb(pb, sb_pb);\n+    if (address) {\n+        icsbrec_port_binding_set_address(isb_pb, address);\n+    }\n+\n+    const struct sbrec_port_binding *crp = find_crp_for_sb_pb(pb, sb_pb);\n+    if (crp && crp->chassis) {\n+        icsbrec_port_binding_set_gateway(isb_pb, crp->chassis->name);\n+    }\n+\n+    update_isb_pb_external_ids(pb, sb_pb, isb_pb);\n+\n+    /* Sync encap so that multiple encaps can be used for the same\n+     * gateway.  However, it is not needed for now, since we don't yet\n+     * support specifying encap type/ip for gateway chassis or ha-chassis\n+     * for logical router port in NB DB, and now encap should always be\n+     * empty.  The sync can be added if we add such support for gateway\n+     * chassis/ha-chassis in NB DB. */\n+}\n+\n+static const struct sbrec_port_binding *\n+find_lsp_in_sb(struct pb_input *pb,\n+               const struct nbrec_logical_switch_port *lsp)\n+{\n+    return find_sb_pb_by_name(pb->sbrec_port_binding_by_name, lsp->name);\n+}\ndiff --git a/ic/en-port-binding.h b/ic/en-port-binding.h\nnew file mode 100644\nindex 000000000..e5f10dced\n--- /dev/null\n+++ b/ic/en-port-binding.h\n@@ -0,0 +1,36 @@\n+#ifndef EN_IC_PORT_BINDING_H\n+#define EN_IC_PORT_BINDING_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+\n+/* OVN includes. */\n+#include \"lib/inc-proc-eng.h\"\n+\n+struct ed_type_port_binding {\n+    struct hmap pb_tnlids;\n+    struct shash switch_all_local_pbs;\n+    struct shash router_all_local_pbs;\n+};\n+\n+struct pb_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_lr_by_name;\n+    struct ovsdb_idl_index *sbrec_port_binding_by_name;\n+    struct ovsdb_idl_index *sbrec_chassis_by_name;\n+    struct ovsdb_idl_index *icsbrec_port_binding_by_az;\n+    struct ovsdb_idl_index *icsbrec_port_binding_by_ts;\n+};\n+\n+void *en_port_binding_init(struct engine_node *, struct engine_arg *);\n+enum engine_node_state en_port_binding_run(struct engine_node *, void *data);\n+void en_port_binding_cleanup(void *data);\n+\n+#endif\ndiff --git a/ic/inc-proc-ic.c b/ic/inc-proc-ic.c\nindex e907a902a..ac360fb9f 100644\n--- a/ic/inc-proc-ic.c\n+++ b/ic/inc-proc-ic.c\n@@ -28,6 +28,7 @@\n #include \"inc-proc-ic.h\"\n #include \"en-ic.h\"\n #include \"en-enum-datapaths.h\"\n+#include \"en-port-binding.h\"\n #include \"unixctl.h\"\n #include \"util.h\"\n \n@@ -160,6 +161,7 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_ic);\n  * avoid sparse errors. */\n static ENGINE_NODE(ic, SB_WRITE);\n static ENGINE_NODE(enum_datapaths);\n+static ENGINE_NODE(port_binding, SB_WRITE);\n \n void inc_proc_ic_init(struct ovsdb_idl_loop *nb,\n                       struct ovsdb_idl_loop *sb,\n@@ -171,7 +173,16 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb,\n     engine_add_input(&en_enum_datapaths, &en_icnb_transit_switch, NULL);\n     engine_add_input(&en_enum_datapaths, &en_icsb_datapath_binding, NULL);\n \n+    engine_add_input(&en_port_binding, &en_icnb_transit_switch, NULL);\n+    engine_add_input(&en_port_binding, &en_icnb_transit_router, NULL);\n+    engine_add_input(&en_port_binding, &en_icsb_port_binding, NULL);\n+    engine_add_input(&en_port_binding, &en_nb_logical_switch, NULL);\n+    engine_add_input(&en_port_binding, &en_sb_port_binding, NULL);\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_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_nb_logical_router, NULL);\n@@ -193,11 +204,11 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb,\n     engine_add_input(&en_ic, &en_icnb_transit_router, NULL);\n     engine_add_input(&en_ic, &en_icnb_transit_router_port, NULL);\n \n+    engine_add_input(&en_ic, &en_icsb_port_binding, NULL);\n     engine_add_input(&en_ic, &en_icsb_ic_sb_global, NULL);\n     engine_add_input(&en_ic, &en_icsb_availability_zone, NULL);\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_port_binding, 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);\ndiff --git a/ic/ovn-ic.c b/ic/ovn-ic.c\nindex 3ad40583e..7bde85854 100644\n--- a/ic/ovn-ic.c\n+++ b/ic/ovn-ic.c\n@@ -70,9 +70,6 @@ static const char *ssl_private_key_file;\n static const char *ssl_certificate_file;\n static const char *ssl_ca_cert_file;\n \n-static const struct sbrec_port_binding * find_sb_pb_by_name(\n-    struct ovsdb_idl_index *sbrec_port_binding_by_name, const char *name);\n-\n \f\n static void\n usage(void)\n@@ -172,7 +169,7 @@ allocate_dp_key(struct hmap *dp_tnlids, bool vxlan_mode, const char *name)\n             &hint);\n }\n \n-static enum ic_port_binding_type\n+enum ic_port_binding_type\n ic_pb_get_type(const struct icsbrec_port_binding *isb_pb)\n {\n     if (isb_pb->type && !strcmp(isb_pb->type, \"transit-router-port\")) {\n@@ -579,16 +576,30 @@ gateway_run(struct engine_context *ctx,\n     shash_destroy(&remote_gws);\n }\n \n-static const struct nbrec_logical_switch *\n-find_ts_in_nb(struct ic_input *ic, char *ts_name)\n+const struct nbrec_logical_router_port *\n+get_lrp_by_lrp_name(struct ic_input *ic, 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_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_destroy_row(lrp_key);\n+\n+    return lrp;\n+}\n+\n+const struct nbrec_logical_switch *\n+find_ts_in_nb(struct ovsdb_idl_index *nbrec_ls_by_name, char *ts_name)\n {\n     const struct nbrec_logical_switch *key =\n-        nbrec_logical_switch_index_init_row(ic->nbrec_ls_by_name);\n+        nbrec_logical_switch_index_init_row(nbrec_ls_by_name);\n     nbrec_logical_switch_index_set_name(key, ts_name);\n \n     const struct nbrec_logical_switch *ls;\n     bool found = false;\n-    NBREC_LOGICAL_SWITCH_FOR_EACH_EQUAL (ls, key, ic->nbrec_ls_by_name) {\n+    NBREC_LOGICAL_SWITCH_FOR_EACH_EQUAL (ls, key, nbrec_ls_by_name) {\n         const char *ls_ts_name = smap_get(&ls->other_config, \"interconn-ts\");\n         if (ls_ts_name && !strcmp(ts_name, ls_ts_name)) {\n             found = true;\n@@ -603,31 +614,21 @@ find_ts_in_nb(struct ic_input *ic, char *ts_name)\n     return NULL;\n }\n \n-static const struct nbrec_logical_router *\n-find_tr_in_nb(struct ic_input *ic, char *tr_name)\n+const struct nbrec_logical_switch_port *\n+get_lsp_by_ts_port_name(struct ovsdb_idl_index *nbrec_port_by_name,\n+                        const char *ts_port_name)\n {\n-    const struct nbrec_logical_router *key =\n-        nbrec_logical_router_index_init_row(ic->nbrec_lr_by_name);\n-    nbrec_logical_router_index_set_name(key, tr_name);\n-\n-    const struct nbrec_logical_router *lr;\n-    bool found = false;\n-    NBREC_LOGICAL_ROUTER_FOR_EACH_EQUAL (lr, key, ic->nbrec_lr_by_name) {\n-        if (smap_get(&lr->options, \"interconn-tr\")) {\n-            found = true;\n-            break;\n-        }\n-    }\n+    const struct nbrec_logical_switch_port *lsp, *key;\n \n-    nbrec_logical_router_index_destroy_row(key);\n-    if (found) {\n-        return lr;\n-    }\n+    key = nbrec_logical_switch_port_index_init_row(nbrec_port_by_name);\n+    nbrec_logical_switch_port_index_set_name(key, ts_port_name);\n+    lsp = nbrec_logical_switch_port_index_find(nbrec_port_by_name, key);\n+    nbrec_logical_switch_port_index_destroy_row(key);\n \n-    return NULL;\n+    return lsp;\n }\n \n-static const struct sbrec_port_binding *\n+const struct sbrec_port_binding *\n find_sb_pb_by_name(struct ovsdb_idl_index *sbrec_port_binding_by_name,\n                    const char *name)\n {\n@@ -642,641 +643,6 @@ find_sb_pb_by_name(struct ovsdb_idl_index *sbrec_port_binding_by_name,\n     return pb;\n }\n \n-static const struct sbrec_port_binding *\n-find_peer_port(struct ic_input *ic,\n-               const struct sbrec_port_binding *sb_pb)\n-{\n-    const char *peer_name = smap_get(&sb_pb->options, \"peer\");\n-    if (!peer_name) {\n-        return NULL;\n-    }\n-\n-    return find_sb_pb_by_name(ic->sbrec_port_binding_by_name, peer_name);\n-}\n-\n-static const struct sbrec_port_binding *\n-find_crp_from_lrp(struct ic_input *ic,\n-                  const struct sbrec_port_binding *lrp_pb)\n-{\n-    char *crp_name = ovn_chassis_redirect_name(lrp_pb->logical_port);\n-\n-    const struct sbrec_port_binding *pb =\n-        find_sb_pb_by_name(ic->sbrec_port_binding_by_name, crp_name);\n-\n-    free(crp_name);\n-    return pb;\n-}\n-\n-static const struct sbrec_port_binding *\n-find_crp_for_sb_pb(struct ic_input *ic,\n-                   const struct sbrec_port_binding *sb_pb)\n-{\n-    const struct sbrec_port_binding *peer = find_peer_port(ic, sb_pb);\n-    if (!peer) {\n-        return NULL;\n-    }\n-\n-    return find_crp_from_lrp(ic, peer);\n-}\n-\n-static const struct nbrec_logical_switch_port *\n-get_lsp_by_ts_port_name(struct ic_input *ic, const char *ts_port_name)\n-{\n-    const struct nbrec_logical_switch_port *lsp, *key;\n-\n-    key = nbrec_logical_switch_port_index_init_row(ic->nbrec_port_by_name);\n-    nbrec_logical_switch_port_index_set_name(key, ts_port_name);\n-    lsp = nbrec_logical_switch_port_index_find(ic->nbrec_port_by_name, key);\n-    nbrec_logical_switch_port_index_destroy_row(key);\n-\n-    return lsp;\n-}\n-\n-static const char *\n-get_lp_address_for_sb_pb(struct ic_input *ic,\n-                         const struct sbrec_port_binding *sb_pb)\n-{\n-    const struct nbrec_logical_switch_port *nb_lsp;\n-\n-    nb_lsp = get_lsp_by_ts_port_name(ic, sb_pb->logical_port);\n-    if (!strcmp(nb_lsp->type, \"switch\")) {\n-        /* Switches always have implicit \"unknown\" address, and IC-SB port\n-         * binding can only have one address specified. */\n-        return \"unknown\";\n-    }\n-\n-    const struct sbrec_port_binding *peer = find_peer_port(ic, sb_pb);\n-    if (!peer) {\n-        return NULL;\n-    }\n-\n-    return peer->n_mac ? *peer->mac : NULL;\n-}\n-\n-static const struct sbrec_chassis *\n-find_sb_chassis(struct ic_input *ic, const char *name)\n-{\n-    const struct sbrec_chassis *key =\n-        sbrec_chassis_index_init_row(ic->sbrec_chassis_by_name);\n-    sbrec_chassis_index_set_name(key, name);\n-\n-    const struct sbrec_chassis *chassis =\n-        sbrec_chassis_index_find(ic->sbrec_chassis_by_name, key);\n-    sbrec_chassis_index_destroy_row(key);\n-\n-    return chassis;\n-}\n-\n-static void\n-sync_lsp_tnl_key(const struct nbrec_logical_switch_port *lsp,\n-                 int64_t isb_tnl_key)\n-{\n-    int64_t tnl_key = smap_get_int(&lsp->options, \"requested-tnl-key\", 0);\n-    if (tnl_key != isb_tnl_key) {\n-        VLOG_DBG(\"Set options:requested-tnl-key %\"PRId64\n-                 \" for lsp %s in NB.\", isb_tnl_key, lsp->name);\n-        char *tnl_key_str = xasprintf(\"%\"PRId64, isb_tnl_key);\n-        nbrec_logical_switch_port_update_options_setkey(lsp,\n-                                                        \"requested-tnl-key\",\n-                                                        tnl_key_str);\n-        free(tnl_key_str);\n-    }\n-\n-}\n-\n-static inline void\n-sync_lrp_tnl_key(const struct nbrec_logical_router_port *lrp,\n-                 int64_t isb_tnl_key)\n-{\n-    int64_t tnl_key = smap_get_int(&lrp->options, \"requested-tnl-key\", 0);\n-    if (tnl_key != isb_tnl_key) {\n-        VLOG_DBG(\"Set options:requested-tnl-key %\" PRId64 \" for lrp %s in NB.\",\n-                 isb_tnl_key, lrp->name);\n-        char *tnl_key_str = xasprintf(\"%\"PRId64, isb_tnl_key);\n-        nbrec_logical_router_port_update_options_setkey(\n-            lrp, \"requested-tnl-key\", tnl_key_str);\n-        free(tnl_key_str);\n-    }\n-}\n-\n-static bool\n-get_router_uuid_by_sb_pb(struct ic_input *ic,\n-                         const struct sbrec_port_binding *sb_pb,\n-                         struct uuid *router_uuid)\n-{\n-    const struct sbrec_port_binding *router_pb = find_peer_port(ic, sb_pb);\n-    if (!router_pb || !router_pb->datapath) {\n-        return NULL;\n-    }\n-\n-    return datapath_get_nb_uuid(router_pb->datapath, router_uuid);\n-}\n-\n-static void\n-update_isb_pb_external_ids(struct ic_input *ic,\n-                           const struct sbrec_port_binding *sb_pb,\n-                           const struct icsbrec_port_binding *isb_pb)\n-{\n-    struct uuid lr_uuid;\n-    if (!get_router_uuid_by_sb_pb(ic, sb_pb, &lr_uuid)) {\n-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\n-        VLOG_WARN_RL(&rl, \"Can't get router uuid for transit switch port %s.\",\n-                     isb_pb->logical_port);\n-        return;\n-    }\n-\n-    struct uuid current_lr_uuid;\n-    if (smap_get_uuid(&isb_pb->external_ids, \"router-id\", &current_lr_uuid) &&\n-        uuid_equals(&lr_uuid, &current_lr_uuid)) {\n-        return;\n-    }\n-\n-    char *uuid_s = xasprintf(UUID_FMT, UUID_ARGS(&lr_uuid));\n-    icsbrec_port_binding_update_external_ids_setkey(isb_pb, \"router-id\",\n-                                                    uuid_s);\n-    free(uuid_s);\n-}\n-\n-/* For each local port:\n- *   - Sync from NB to ISB.\n- *   - Sync gateway from SB to ISB.\n- *   - Sync tunnel key from ISB to NB.\n- */\n-static void\n-sync_local_port(struct ic_input *ic,\n-                const struct icsbrec_port_binding *isb_pb,\n-                const struct sbrec_port_binding *sb_pb,\n-                const struct nbrec_logical_switch_port *lsp)\n-{\n-    /* Sync address from NB to ISB */\n-    const char *address = get_lp_address_for_sb_pb(ic, sb_pb);\n-    if (!address) {\n-        VLOG_DBG(\"Can't get router/switch port address for logical\"\n-                 \" switch port %s\", sb_pb->logical_port);\n-        if (isb_pb->address[0]) {\n-            icsbrec_port_binding_set_address(isb_pb, \"\");\n-        }\n-    } else {\n-        if (strcmp(address, isb_pb->address)) {\n-            icsbrec_port_binding_set_address(isb_pb, address);\n-        }\n-    }\n-\n-    /* Sync gateway from SB to ISB */\n-    const struct sbrec_port_binding *crp = find_crp_for_sb_pb(ic, sb_pb);\n-    if (crp && crp->chassis) {\n-        if (strcmp(crp->chassis->name, isb_pb->gateway)) {\n-            icsbrec_port_binding_set_gateway(isb_pb, crp->chassis->name);\n-        }\n-    } else if (!strcmp(lsp->type, \"switch\") && sb_pb->chassis) {\n-        if (strcmp(sb_pb->chassis->name, isb_pb->gateway)) {\n-            icsbrec_port_binding_set_gateway(isb_pb, sb_pb->chassis->name);\n-        }\n-    } else {\n-        if (isb_pb->gateway[0]) {\n-            icsbrec_port_binding_set_gateway(isb_pb, \"\");\n-        }\n-    }\n-\n-    /* Sync external_ids:router-id to ISB */\n-    update_isb_pb_external_ids(ic, sb_pb, isb_pb);\n-\n-    /* Sync back tunnel key from ISB to NB */\n-    sync_lsp_tnl_key(lsp, isb_pb->tunnel_key);\n-}\n-\n-/* For each remote port:\n- *   - Sync from ISB to NB\n- *   - Sync gateway from ISB to SB\n- */\n-static void\n-sync_remote_port(struct ic_input *ic,\n-                 const struct icsbrec_port_binding *isb_pb,\n-                 const struct nbrec_logical_switch_port *lsp,\n-                 const struct sbrec_port_binding *sb_pb)\n-{\n-    /* Sync address from ISB to NB */\n-    if (isb_pb->address[0]) {\n-        if (lsp->n_addresses != 1 ||\n-            strcmp(isb_pb->address, lsp->addresses[0])) {\n-            nbrec_logical_switch_port_set_addresses(\n-                lsp, (const char **)&isb_pb->address, 1);\n-        }\n-    } else {\n-        if (lsp->n_addresses != 0) {\n-            nbrec_logical_switch_port_set_addresses(lsp, NULL, 0);\n-        }\n-    }\n-\n-    /* Sync tunnel key from ISB to NB */\n-    sync_lsp_tnl_key(lsp, isb_pb->tunnel_key);\n-\n-    /* Skip port binding if it is already requested by the CMS. */\n-    if (smap_get(&lsp->options, \"requested-chassis\")) {\n-        return;\n-    }\n-\n-    /* Sync gateway from ISB to SB */\n-    if (isb_pb->gateway[0]) {\n-        if (!sb_pb->chassis || strcmp(sb_pb->chassis->name, isb_pb->gateway)) {\n-            const struct sbrec_chassis *chassis =\n-                find_sb_chassis(ic, isb_pb->gateway);\n-            if (!chassis) {\n-                VLOG_DBG(\"Chassis %s is not found in SB, syncing from ISB \"\n-                         \"to SB skipped for logical port %s.\",\n-                         isb_pb->gateway, lsp->name);\n-                return;\n-            }\n-            sbrec_port_binding_set_chassis(sb_pb, chassis);\n-        }\n-    } else {\n-        if (sb_pb->chassis) {\n-            sbrec_port_binding_set_chassis(sb_pb, NULL);\n-        }\n-    }\n-}\n-\n-/* For each remote port:\n- *   - Sync from ISB to NB\n- */\n-static void\n-sync_router_port(const struct icsbrec_port_binding *isb_pb,\n-                 const struct icnbrec_transit_router_port *trp,\n-                 const struct nbrec_logical_router_port *lrp)\n-{\n-    /* Sync from ICNB to NB */\n-    if (trp->chassis[0]) {\n-        const char *chassis_name =\n-            smap_get_def(&lrp->options, \"requested-chassis\", \"\");\n-        if (strcmp(trp->chassis, chassis_name)) {\n-            nbrec_logical_router_port_update_options_setkey(\n-                lrp, \"requested-chassis\", trp->chassis);\n-        }\n-    } else {\n-        nbrec_logical_router_port_update_options_delkey(\n-            lrp, \"requested-chassis\");\n-    }\n-\n-    if (strcmp(trp->mac, lrp->mac)) {\n-        nbrec_logical_router_port_set_mac(lrp, trp->mac);\n-    }\n-\n-    bool sync_networks = false;\n-    if (trp->n_networks != lrp->n_networks) {\n-        sync_networks = true;\n-    } else {\n-        for (size_t i = 0; i < trp->n_networks; i++) {\n-            if (strcmp(trp->networks[i], lrp->networks[i])) {\n-                sync_networks |= true;\n-                break;\n-            }\n-        }\n-    }\n-\n-    if (sync_networks) {\n-        nbrec_logical_router_port_set_networks(\n-            lrp, (const char **) trp->networks, trp->n_networks);\n-    }\n-\n-    /* Sync tunnel key from ISB to NB */\n-    sync_lrp_tnl_key(lrp, isb_pb->tunnel_key);\n-}\n-\n-static void\n-create_nb_lsp(struct engine_context *ctx,\n-              const struct icsbrec_port_binding *isb_pb,\n-              const struct nbrec_logical_switch *ls)\n-{\n-    const struct nbrec_logical_switch_port *lsp =\n-        nbrec_logical_switch_port_insert(ctx->ovnnb_idl_txn);\n-    nbrec_logical_switch_port_set_name(lsp, isb_pb->logical_port);\n-    nbrec_logical_switch_port_set_type(lsp, \"remote\");\n-\n-    bool up = true;\n-    nbrec_logical_switch_port_set_up(lsp, &up, 1);\n-\n-    if (isb_pb->address[0]) {\n-        nbrec_logical_switch_port_set_addresses(\n-            lsp, (const char **)&isb_pb->address, 1);\n-    }\n-    sync_lsp_tnl_key(lsp, isb_pb->tunnel_key);\n-    nbrec_logical_switch_update_ports_addvalue(ls, lsp);\n-}\n-\n-static uint32_t\n-allocate_port_key(struct hmap *pb_tnlids)\n-{\n-    static uint32_t hint;\n-    return ovn_allocate_tnlid(pb_tnlids, \"transit port\",\n-                              1, (1u << 15) - 1, &hint);\n-}\n-\n-static const struct icsbrec_port_binding *\n-create_isb_pb(struct engine_context *ctx, const char *logical_port,\n-              const struct icsbrec_availability_zone *az, const char *ts_name,\n-              const struct uuid *nb_ic_uuid, const char *type,\n-              struct hmap *pb_tnlids)\n-{\n-    uint32_t pb_tnl_key = allocate_port_key(pb_tnlids);\n-    if (!pb_tnl_key) {\n-        return NULL;\n-    }\n-\n-    const struct icsbrec_port_binding *isb_pb =\n-        icsbrec_port_binding_insert(ctx->ovnisb_idl_txn);\n-    icsbrec_port_binding_set_availability_zone(isb_pb, az);\n-    icsbrec_port_binding_set_transit_switch(isb_pb, ts_name);\n-    icsbrec_port_binding_set_logical_port(isb_pb, logical_port);\n-    icsbrec_port_binding_set_tunnel_key(isb_pb, pb_tnl_key);\n-    icsbrec_port_binding_set_nb_ic_uuid(isb_pb, nb_ic_uuid, 1);\n-    icsbrec_port_binding_set_type(isb_pb, type);\n-    return isb_pb;\n-}\n-\n-static const struct nbrec_logical_router_port *\n-get_lrp_by_lrp_name(struct ic_input *ic, 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_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_destroy_row(lrp_key);\n-\n-    return lrp;\n-}\n-\n-static bool\n-trp_is_remote(struct ic_input *ic, const char *chassis_name)\n-{\n-    if (chassis_name) {\n-        const struct sbrec_chassis *chassis =\n-            find_sb_chassis(ic, chassis_name);\n-        if (chassis) {\n-            return smap_get_bool(&chassis->other_config, \"is-remote\", false);\n-        } else {\n-            return true;\n-        }\n-    }\n-\n-    return false;\n-}\n-\n-static struct nbrec_logical_router_port *\n-lrp_create(struct engine_context *ctx, const struct nbrec_logical_router *lr,\n-           const struct icnbrec_transit_router_port *trp)\n-{\n-    struct nbrec_logical_router_port *lrp =\n-        nbrec_logical_router_port_insert(ctx->ovnnb_idl_txn);\n-    nbrec_logical_router_port_set_name(lrp, trp->name);\n-\n-    nbrec_logical_router_port_update_options_setkey(lrp, \"interconn-tr\",\n-                                                    trp->name);\n-    nbrec_logical_router_update_ports_addvalue(lr, lrp);\n-    return lrp;\n-}\n-\n-static void\n-sync_ts_isb_pb(struct ic_input *ic, const struct sbrec_port_binding *sb_pb,\n-               const struct icsbrec_port_binding *isb_pb)\n-{\n-    const char *address = get_lp_address_for_sb_pb(ic, sb_pb);\n-    if (address) {\n-        icsbrec_port_binding_set_address(isb_pb, address);\n-    }\n-\n-    const struct sbrec_port_binding *crp = find_crp_for_sb_pb(ic, sb_pb);\n-    if (crp && crp->chassis) {\n-        icsbrec_port_binding_set_gateway(isb_pb, crp->chassis->name);\n-    }\n-\n-    update_isb_pb_external_ids(ic, sb_pb, isb_pb);\n-\n-    /* XXX: Sync encap so that multiple encaps can be used for the same\n-     * gateway.  However, it is not needed for now, since we don't yet\n-     * support specifying encap type/ip for gateway chassis or ha-chassis\n-     * for logical router port in NB DB, and now encap should always be\n-     * empty.  The sync can be added if we add such support for gateway\n-     * chassis/ha-chassis in NB DB. */\n-}\n-\n-static const struct sbrec_port_binding *\n-find_lsp_in_sb(struct ic_input *ic,\n-               const struct nbrec_logical_switch_port *lsp)\n-{\n-    return find_sb_pb_by_name(ic->sbrec_port_binding_by_name, lsp->name);\n-}\n-\n-static void\n-port_binding_run(struct engine_context *ctx,\n-                 struct ic_input *ic)\n-{\n-    if (!ctx->ovnisb_idl_txn || !ctx->ovnnb_idl_txn\n-        || !ctx->ovnsb_idl_txn) {\n-        return;\n-    }\n-\n-    struct shash switch_all_local_pbs =\n-        SHASH_INITIALIZER(&switch_all_local_pbs);\n-    struct shash router_all_local_pbs =\n-        SHASH_INITIALIZER(&router_all_local_pbs);\n-    struct hmap pb_tnlids = HMAP_INITIALIZER(&pb_tnlids);\n-    struct shash_node *node;\n-\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-    ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,\n-                                         ic->icsbrec_port_binding_by_az) {\n-        ic_pb_get_type(isb_pb) != IC_ROUTER_PORT\n-            ? shash_add(&switch_all_local_pbs, isb_pb->logical_port, isb_pb)\n-            : shash_add(&router_all_local_pbs, isb_pb->logical_port, isb_pb);\n-\n-        ovn_add_tnlid(&pb_tnlids, isb_pb->tunnel_key);\n-    }\n-    icsbrec_port_binding_index_destroy_row(isb_pb_key);\n-\n-    const struct sbrec_port_binding *sb_pb;\n-    const struct icnbrec_transit_switch *ts;\n-    ICNBREC_TRANSIT_SWITCH_TABLE_FOR_EACH (ts,\n-                                           ic->icnbrec_transit_switch_table) {\n-        const struct nbrec_logical_switch *ls = find_ts_in_nb(ic, ts->name);\n-        if (!ls) {\n-            VLOG_DBG(\"Transit switch %s not found in NB.\", ts->name);\n-            continue;\n-        }\n-        struct shash local_pbs = SHASH_INITIALIZER(&local_pbs);\n-        struct shash remote_pbs = SHASH_INITIALIZER(&remote_pbs);\n-\n-        isb_pb_key = icsbrec_port_binding_index_init_row(\n-            ic->icsbrec_port_binding_by_ts);\n-        icsbrec_port_binding_index_set_transit_switch(isb_pb_key, ts->name);\n-\n-        ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,\n-                                             ic->icsbrec_port_binding_by_ts) {\n-            if (isb_pb->availability_zone == ic->runned_az) {\n-                shash_add(&local_pbs, isb_pb->logical_port, isb_pb);\n-                shash_find_and_delete(&switch_all_local_pbs,\n-                                      isb_pb->logical_port);\n-            } else {\n-                shash_add(&remote_pbs, isb_pb->logical_port, isb_pb);\n-            }\n-        }\n-        icsbrec_port_binding_index_destroy_row(isb_pb_key);\n-\n-        const struct nbrec_logical_switch_port *lsp;\n-        for (int i = 0; i < ls->n_ports; i++) {\n-            lsp = ls->ports[i];\n-\n-            if (!strcmp(lsp->type, \"router\")\n-                || !strcmp(lsp->type, \"switch\")) {\n-                /* The port is local. */\n-                sb_pb = find_lsp_in_sb(ic, lsp);\n-                if (!sb_pb) {\n-                    continue;\n-                }\n-                isb_pb = shash_find_and_delete(&local_pbs, lsp->name);\n-                if (!isb_pb) {\n-                    isb_pb = create_isb_pb(\n-                        ctx, sb_pb->logical_port, ic->runned_az, ts->name,\n-                        &ts->header_.uuid, \"transit-switch-port\", &pb_tnlids);\n-                    sync_ts_isb_pb(ic, sb_pb, isb_pb);\n-                } else {\n-                    sync_local_port(ic, isb_pb, sb_pb, lsp);\n-                }\n-\n-                if (isb_pb->type) {\n-                    icsbrec_port_binding_set_type(isb_pb,\n-                                                  \"transit-switch-port\");\n-                }\n-\n-                if (isb_pb->nb_ic_uuid) {\n-                    icsbrec_port_binding_set_nb_ic_uuid(isb_pb,\n-                                                        &ts->header_.uuid, 1);\n-                }\n-            } else if (!strcmp(lsp->type, \"remote\")) {\n-                /* The port is remote. */\n-                isb_pb = shash_find_and_delete(&remote_pbs, lsp->name);\n-                if (!isb_pb) {\n-                    nbrec_logical_switch_update_ports_delvalue(ls, lsp);\n-                } else {\n-                    sb_pb = find_lsp_in_sb(ic, lsp);\n-                    if (!sb_pb) {\n-                        continue;\n-                    }\n-                    sync_remote_port(ic, isb_pb, lsp, sb_pb);\n-                }\n-            } else {\n-                VLOG_DBG(\"Ignore lsp %s on ts %s with type %s.\",\n-                         lsp->name, ts->name, lsp->type);\n-            }\n-        }\n-\n-        /* Delete extra port-binding from ISB */\n-        SHASH_FOR_EACH (node, &local_pbs) {\n-            icsbrec_port_binding_delete(node->data);\n-        }\n-\n-        /* Create lsp in NB for remote ports */\n-        SHASH_FOR_EACH (node, &remote_pbs) {\n-            create_nb_lsp(ctx, node->data, ls);\n-        }\n-\n-        shash_destroy(&local_pbs);\n-        shash_destroy(&remote_pbs);\n-    }\n-\n-    SHASH_FOR_EACH (node, &switch_all_local_pbs) {\n-        icsbrec_port_binding_delete(node->data);\n-    }\n-    shash_destroy(&switch_all_local_pbs);\n-\n-    const struct icnbrec_transit_router *tr;\n-    ICNBREC_TRANSIT_ROUTER_TABLE_FOR_EACH (tr,\n-                                           ic->icnbrec_transit_router_table) {\n-        const struct nbrec_logical_router *lr = find_tr_in_nb(ic, tr->name);\n-        if (!lr) {\n-            VLOG_DBG(\"Transit router %s not found in NB.\", tr->name);\n-            continue;\n-        }\n-\n-        struct shash nb_ports = SHASH_INITIALIZER(&nb_ports);\n-        struct shash local_pbs = SHASH_INITIALIZER(&local_pbs);\n-        struct shash remote_pbs = SHASH_INITIALIZER(&remote_pbs);\n-\n-        for (size_t i = 0; i < lr->n_ports; i++) {\n-            const struct nbrec_logical_router_port *lrp = lr->ports[i];\n-            if (smap_get_def(&lrp->options, \"interconn-tr\", NULL)) {\n-                shash_add(&nb_ports, lrp->name, lrp);\n-            }\n-        }\n-\n-        isb_pb_key = icsbrec_port_binding_index_init_row(\n-            ic->icsbrec_port_binding_by_ts);\n-        icsbrec_port_binding_index_set_transit_switch(isb_pb_key, tr->name);\n-\n-        ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,\n-                                             ic->icsbrec_port_binding_by_ts) {\n-            if (isb_pb->availability_zone == ic->runned_az) {\n-                shash_add(&local_pbs, isb_pb->logical_port, isb_pb);\n-                shash_find_and_delete(&router_all_local_pbs,\n-                                      isb_pb->logical_port);\n-            } else {\n-                shash_add(&remote_pbs, isb_pb->logical_port, isb_pb);\n-            }\n-        }\n-        icsbrec_port_binding_index_destroy_row(isb_pb_key);\n-\n-        for (size_t i = 0; i < tr->n_ports; i++) {\n-            const struct icnbrec_transit_router_port *trp = tr->ports[i];\n-\n-            if (trp_is_remote(ic, trp->chassis)) {\n-                isb_pb = shash_find_and_delete(&remote_pbs, trp->name);\n-            } else {\n-                isb_pb = shash_find_and_delete(&local_pbs, trp->name);\n-                if (!isb_pb) {\n-                    isb_pb = create_isb_pb(ctx, trp->name, ic->runned_az,\n-                                           tr->name, &tr->header_.uuid,\n-                                           \"transit-router-port\", &pb_tnlids);\n-                    icsbrec_port_binding_set_address(isb_pb, trp->mac);\n-                }\n-            }\n-\n-            /* Don't allow remote ports to create NB LRP until ICSB entry is\n-             * created in the appropriate AZ. */\n-            if (isb_pb) {\n-                const struct nbrec_logical_router_port *lrp =\n-                    shash_find_and_delete(&nb_ports, trp->name);\n-                if (!lrp) {\n-                    lrp = lrp_create(ctx, lr, trp);\n-                }\n-\n-                sync_router_port(isb_pb, trp, lrp);\n-            }\n-        }\n-\n-        SHASH_FOR_EACH(node, &nb_ports) {\n-            nbrec_logical_router_port_delete(node->data);\n-            nbrec_logical_router_update_ports_delvalue(lr, node->data);\n-        }\n-\n-        shash_destroy(&nb_ports);\n-        shash_destroy(&local_pbs);\n-        shash_destroy(&remote_pbs);\n-    }\n-\n-    SHASH_FOR_EACH (node, &router_all_local_pbs) {\n-        icsbrec_port_binding_delete(node->data);\n-    }\n-\n-    ovn_destroy_tnlids(&pb_tnlids);\n-    shash_destroy(&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@@ -1948,7 +1314,7 @@ 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, ts_port_name);\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@@ -1962,7 +1328,7 @@ find_lrp_of_nexthop(struct ic_input *ic,\n {\n     const struct nbrec_logical_router_port *lrp;\n     const struct nbrec_logical_switch *ls;\n-    ls = find_ts_in_nb(ic, isb_route->transit_switch);\n+    ls = find_ts_in_nb(ic->nbrec_ls_by_name, isb_route->transit_switch);\n     if (!ls) {\n         return NULL;\n     }\n@@ -2502,7 +1868,8 @@ route_run(struct engine_context *ctx,\n         }\n         const struct nbrec_logical_switch_port *nb_lsp;\n \n-        nb_lsp = get_lsp_by_ts_port_name(ic, isb_pb->logical_port);\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@@ -3118,7 +2485,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-    port_binding_run(eng_ctx, input_data);\n     route_run(eng_ctx, input_data);\n     sync_service_monitor(eng_ctx, input_data);\n }\n@@ -3524,6 +2890,7 @@ main(int argc, char *argv[])\n     stopwatch_create(OVN_IC_LOOP_STOPWATCH_NAME, SW_MS);\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 \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 9b0009274..ff6317786 100644\n--- a/ic/ovn-ic.h\n+++ b/ic/ovn-ic.h\n@@ -55,8 +55,6 @@ struct ic_input {\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_port_binding_by_ts;\n-    struct ovsdb_idl_index *icsbrec_port_binding_by_ts_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@@ -76,16 +74,29 @@ struct ic_state {\n     bool paused;\n };\n \n+struct icsbrec_port_binding;\n+\n 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+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 *\n+    find_ts_in_nb(struct ovsdb_idl_index *nbrec_ls_by_name, char *ts_name);\n+const struct nbrec_logical_switch_port *\n+    get_lsp_by_ts_port_name(struct ovsdb_idl_index *nbrec_port_by_name,\n+                            const char *ts_port_name);\n+const struct sbrec_port_binding *\n+    find_sb_pb_by_name(struct ovsdb_idl_index *sbrec_port_binding_by_name,\n+                       const char *name);\n+enum ic_port_binding_type\n+    ic_pb_get_type(const struct icsbrec_port_binding *isb_pb);\n const struct icsbrec_availability_zone *\n-    az_run(struct ovsdb_idl *ovnnb_idl,\n-           struct ovsdb_idl *ovnisb_idl,\n+    az_run(struct ovsdb_idl *ovnnb_idl, struct ovsdb_idl *ovnisb_idl,\n            struct ovsdb_idl_txn *ovnisb_idl_txn);\n-\n-void ovn_db_run(struct ic_input *input_data,\n-                struct ic_data *ic_data,\n+void ovn_db_run(struct ic_input *input_data, struct ic_data *ic_data,\n                 struct engine_context *eng_ctx);\n \n #endif /* OVN_IC_H */\ndiff --git a/lib/stopwatch-names.h b/lib/stopwatch-names.h\nindex 4eef534ed..bf5da699c 100644\n--- a/lib/stopwatch-names.h\n+++ b/lib/stopwatch-names.h\n@@ -43,5 +43,6 @@\n #define OVN_IC_LOOP_STOPWATCH_NAME \"ovn-ic-loop\"\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 \n #endif\n","prefixes":["ovs-dev","v0","3/9"]}