diff mbox series

[ovs-dev] controller: Add nat_addresses engine.

Message ID ZyyOTk4UaNmApmXp@SIT-SDELAP1634.int.lidl.net
State New
Headers show
Series [ovs-dev] controller: Add nat_addresses engine. | expand

Commit Message

Max Lamprecht Nov. 7, 2024, 9:54 a.m. UTC
This patch moves the nat_addresses calculation to an own engine node.

Previously the nat_address calculation was executed at every pinctrl_run
(send_garp_rarp_prepare). This can cause high cpu usage and delayed
actions on gateway-chassis(500 LRPs+).

perf trace:
- 98.83% main
  - 79.69% pinctrl_run
    - 77.71% send_garp_rarp_prepare
      - 55.49% get_nat_addresses_and_keys
        - 36.76% consider_nat_address
        - 18.13% lport_lookup_by_name
      - 13.58% sset_destroy

Co-authored-by: Ihtisham ul Haq <Ihtisham.ul_Haq@stackit.cloud>
Signed-off-by: Ihtisham ul Haq <Ihtisham.ul_Haq@stackit.cloud>
Signed-off-by: Max Lamprecht <max.lamprecht@stackit.cloud>
---
 controller/ovn-controller.c | 338 +++++++++++++++++++++++++++++++-
 controller/pinctrl.c        | 376 ++++++------------------------------
 controller/pinctrl.h        |  14 +-
 3 files changed, 402 insertions(+), 326 deletions(-)
diff mbox series

Patch

diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index c40fb3d43..ec5e2caa7 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -3343,6 +3343,318 @@  en_bfd_chassis_cleanup(void *data OVS_UNUSED){
     sset_destroy(&bfd_chassis->bfd_chassis);
 }
 
+static void *
+en_nat_addresses_init(struct engine_node *node OVS_UNUSED,
+                   struct engine_arg *arg OVS_UNUSED)
+{
+    struct ed_type_nat_addresses *data = xzalloc(sizeof *data);
+    shash_init(&data->nat_addresses);
+    sset_init(&data->localnet_vifs);
+    sset_init(&data->local_l3gw_ports);
+    sset_init(&data->nat_ip_keys);
+    return data;
+}
+
+/* Extracts the mac, IPv4 and IPv6 addresses, and logical port from
+ * 'addresses' which should be of the format 'MAC [IP1 IP2 ..]
+ * [is_chassis_resident("LPORT_NAME")]', where IPn should be a valid IPv4
+ * or IPv6 address, and stores them in the 'ipv4_addrs' and 'ipv6_addrs'
+ * fields of 'laddrs'.  The logical port name is stored in 'lport'.
+ *
+ * Returns true if at least 'MAC' is found in 'address', false otherwise.
+ *
+ * The caller must call destroy_lport_addresses() and free(*lport). */
+static bool
+extract_addresses_with_port(const char *addresses,
+                            struct lport_addresses *laddrs,
+                            char **lport)
+{
+    int ofs;
+    if (!extract_addresses(addresses, laddrs, &ofs)) {
+        return false;
+    } else if (!addresses[ofs]) {
+        return true;
+    }
+
+    struct lexer lexer;
+    lexer_init(&lexer, addresses + ofs);
+    lexer_get(&lexer);
+
+    if (lexer.error || lexer.token.type != LEX_T_ID
+        || !lexer_match_id(&lexer, "is_chassis_resident")) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+        VLOG_INFO_RL(&rl, "invalid syntax '%s' in address", addresses);
+        lexer_destroy(&lexer);
+        return true;
+    }
+
+    if (!lexer_match(&lexer, LEX_T_LPAREN)) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+        VLOG_INFO_RL(&rl, "Syntax error: expecting '(' after "
+                          "'is_chassis_resident' in address '%s'", addresses);
+        lexer_destroy(&lexer);
+        return false;
+    }
+
+    if (lexer.token.type != LEX_T_STRING) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+        VLOG_INFO_RL(&rl,
+                    "Syntax error: expecting quoted string after "
+                    "'is_chassis_resident' in address '%s'", addresses);
+        lexer_destroy(&lexer);
+        return false;
+    }
+
+    *lport = xstrdup(lexer.token.s);
+
+    lexer_get(&lexer);
+    if (!lexer_match(&lexer, LEX_T_RPAREN)) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+        VLOG_INFO_RL(&rl, "Syntax error: expecting ')' after quoted string in "
+                          "'is_chassis_resident()' in address '%s'",
+                          addresses);
+        lexer_destroy(&lexer);
+        return false;
+    }
+
+    lexer_destroy(&lexer);
+    return true;
+}
+
+static void
+consider_nat_address(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+                     const char *nat_address,
+                     const struct sbrec_port_binding *pb,
+                     struct sset *nat_address_keys,
+                     const struct sbrec_chassis *chassis,
+                     const struct sset *active_tunnels,
+                     struct shash *nat_addresses)
+{
+    struct lport_addresses *laddrs = xmalloc(sizeof *laddrs);
+    char *lport = NULL;
+    if (!extract_addresses_with_port(nat_address, laddrs, &lport)
+        || (!lport && !strcmp(pb->type, "patch"))
+        || (lport && !lport_is_chassis_resident(
+                sbrec_port_binding_by_name, chassis,
+                active_tunnels, lport))) {
+        destroy_lport_addresses(laddrs);
+        free(laddrs);
+        free(lport);
+        return;
+    }
+    free(lport);
+
+    int i;
+    for (i = 0; i < laddrs->n_ipv4_addrs; i++) {
+        char *name = xasprintf("%s-%s", pb->logical_port,
+                                        laddrs->ipv4_addrs[i].addr_s);
+        sset_add(nat_address_keys, name);
+        free(name);
+    }
+    if (laddrs->n_ipv4_addrs == 0) {
+        char *name = xasprintf("%s-noip", pb->logical_port);
+        sset_add(nat_address_keys, name);
+        free(name);
+    }
+    shash_add(nat_addresses, pb->logical_port, laddrs);
+}
+
+static void
+get_nat_addresses_and_keys(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+                           struct sset *nat_address_keys,
+                           struct sset *local_l3gw_ports,
+                           const struct sbrec_chassis *chassis,
+                           const struct sset *active_tunnels,
+                           struct shash *nat_addresses)
+{
+    const char *gw_port;
+    SSET_FOR_EACH(gw_port, local_l3gw_ports) {
+        const struct sbrec_port_binding *pb;
+
+        pb = lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
+        if (!pb) {
+            continue;
+        }
+
+        if (pb->n_nat_addresses) {
+            for (int i = 0; i < pb->n_nat_addresses; i++) {
+                consider_nat_address(sbrec_port_binding_by_name,
+                                     pb->nat_addresses[i], pb,
+                                     nat_address_keys, chassis,
+                                     active_tunnels,
+                                     nat_addresses);
+            }
+        } else {
+            /* Continue to support options:nat-addresses for version
+             * upgrade. */
+            const char *nat_addresses_options = smap_get(&pb->options,
+                                                         "nat-addresses");
+            if (nat_addresses_options) {
+                consider_nat_address(sbrec_port_binding_by_name,
+                                     nat_addresses_options, pb,
+                                     nat_address_keys, chassis,
+                                     active_tunnels,
+                                     nat_addresses);
+            }
+        }
+    }
+}
+
+/* Get localnet vifs, local l3gw ports and ofport for localnet patch ports. */
+static void
+get_localnet_vifs_l3gwports(
+    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+    struct ovsdb_idl_index *sbrec_port_binding_by_name,
+    const struct ovsrec_bridge *br_int,
+    const struct sbrec_chassis *chassis,
+    const struct hmap *local_datapaths,
+    struct sset *localnet_vifs,
+    struct sset *local_l3gw_ports)
+{
+    for (int i = 0; i < br_int->n_ports; i++) {
+        const struct ovsrec_port *port_rec = br_int->ports[i];
+        if (!strcmp(port_rec->name, br_int->name)) {
+            continue;
+        }
+        const char *tunnel_id = smap_get(&port_rec->external_ids,
+                                          "ovn-chassis-id");
+        if (tunnel_id &&
+                encaps_tunnel_id_match(tunnel_id, chassis->name, NULL, NULL)) {
+            continue;
+        }
+        const char *localnet = smap_get(&port_rec->external_ids,
+                                        "ovn-localnet-port");
+        if (localnet) {
+            continue;
+        }
+        for (int j = 0; j < port_rec->n_interfaces; j++) {
+            const struct ovsrec_interface *iface_rec = port_rec->interfaces[j];
+            if (!iface_rec->n_ofport) {
+                continue;
+            }
+            /* Get localnet vif. */
+            const char *iface_id = smap_get(&iface_rec->external_ids,
+                                            "iface-id");
+            if (!iface_id) {
+                continue;
+            }
+            const struct sbrec_port_binding *pb
+                = lport_lookup_by_name(sbrec_port_binding_by_name, iface_id);
+            if (!pb || pb->chassis != chassis) {
+                continue;
+            }
+            if (!iface_rec->link_state ||
+                    strcmp(iface_rec->link_state, "up")) {
+                continue;
+            }
+            struct local_datapath *ld
+                = get_local_datapath(local_datapaths,
+                                     pb->datapath->tunnel_key);
+            if (ld && ld->localnet_port) {
+                sset_add(localnet_vifs, iface_id);
+            }
+        }
+    }
+
+    struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
+        sbrec_port_binding_by_datapath);
+
+    const struct local_datapath *ld;
+    HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
+        const struct sbrec_port_binding *pb;
+
+        if (!ld->localnet_port) {
+            continue;
+        }
+
+        /* Get l3gw ports.  Consider port bindings with type "l3gateway"
+         * that connect to gateway routers (if local), and consider port
+         * bindings of type "patch" since they might connect to
+         * distributed gateway ports with NAT addresses. */
+
+        sbrec_port_binding_index_set_datapath(target, ld->datapath);
+        SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
+                                           sbrec_port_binding_by_datapath) {
+            if ((!strcmp(pb->type, "l3gateway") && pb->chassis == chassis)
+                || !strcmp(pb->type, "patch")) {
+                sset_add(local_l3gw_ports, pb->logical_port);
+            }
+        }
+    }
+    sbrec_port_binding_index_destroy_row(target);
+}
+
+static void
+en_nat_addresses_run(struct engine_node *node, void *data OVS_UNUSED)
+{
+    struct ed_type_nat_addresses *nat_addresses = data;
+    const struct ovsrec_open_vswitch_table *ovs_table =
+        EN_OVSDB_GET(engine_get_input("OVS_open_vswitch", node));
+    const char *chassis_id = get_ovs_chassis_id(ovs_table);
+    struct ovsdb_idl_index *sbrec_chassis_by_name =
+        engine_ovsdb_node_get_index(
+            engine_get_input("SB_chassis", node),
+            "name");
+    const struct sbrec_chassis *chassis
+        = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
+
+    struct ovsdb_idl_index *sbrec_pb_by_datapath =
+            engine_ovsdb_node_get_index(
+                    engine_get_input("SB_port_binding", node),
+                    "datapath");
+    struct ovsdb_idl_index *sbrec_pb_by_name =
+            engine_ovsdb_node_get_index(
+                    engine_get_input("SB_port_binding", node),
+                    "name");
+    const struct ovsrec_bridge_table *bridge_table =
+        EN_OVSDB_GET(engine_get_input("OVS_bridge", node));
+    const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
+
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+    const struct hmap *local_datapaths = &rt_data->local_datapaths;
+    const struct sset *active_tunnels = &rt_data->active_tunnels;
+
+    struct shash_node *nat_node;
+    SHASH_FOR_EACH(nat_node, &nat_addresses->nat_addresses) {
+        struct lport_addresses *laddrs = nat_node->data;
+        destroy_lport_addresses(laddrs);
+        free(laddrs);
+    }
+    shash_clear(&nat_addresses->nat_addresses);
+    sset_clear(&nat_addresses->localnet_vifs);
+    sset_clear(&nat_addresses->local_l3gw_ports);
+    sset_clear(&nat_addresses->nat_ip_keys);
+
+    get_localnet_vifs_l3gwports(sbrec_pb_by_datapath,
+                                sbrec_pb_by_name,
+                                br_int, chassis, local_datapaths,
+                                &nat_addresses->localnet_vifs, &nat_addresses->local_l3gw_ports);
+
+    get_nat_addresses_and_keys(sbrec_pb_by_name,
+                               &nat_addresses->nat_ip_keys, &nat_addresses->local_l3gw_ports,
+                               chassis, active_tunnels,
+                               &nat_addresses->nat_addresses);
+
+    engine_set_node_state(node, EN_UPDATED);
+}
+
+static bool
+nat_addresses_change_handler(struct engine_node *node, void *data)
+{
+    en_nat_addresses_run(node, data);
+    return true;
+}
+
+static void
+en_nat_addresses_cleanup(void *data OVS_UNUSED){
+    struct ed_type_nat_addresses *nat_addresses = data;
+    shash_destroy(&nat_addresses->nat_addresses);
+    sset_destroy(&nat_addresses->localnet_vifs);
+    sset_destroy(&nat_addresses->local_l3gw_ports);
+    sset_destroy(&nat_addresses->nat_ip_keys);
+}
+
 /* Engine node which is used to handle the Non VIF data like
  *   - OVS patch ports
  *   - Tunnel ports and the related chassis information.
@@ -4762,6 +5074,14 @@  controller_output_bfd_chassis_handler(struct engine_node *node,
     return true;
 }
 
+static bool
+controller_output_nat_addresses_handler(struct engine_node *node,
+                                        void *data OVS_UNUSED)
+{
+    engine_set_node_state(node, EN_UPDATED);
+    return true;
+}
+
 /* Handles sbrec_chassis changes.
  * If a new chassis is added or removed return false, so that
  * flows are recomputed.  For any updates, there is no need for
@@ -5075,6 +5395,7 @@  main(int argc, char *argv[])
     ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lb_data, "lb_data");
     ENGINE_NODE(mac_cache, "mac_cache");
     ENGINE_NODE(bfd_chassis, "bfd_chassis");
+    ENGINE_NODE(nat_addresses, "nat_addresses");
 
 #define SB_NODE(NAME, NAME_STR) ENGINE_NODE_SB(NAME, NAME_STR);
     SB_NODES
@@ -5119,6 +5440,12 @@  main(int argc, char *argv[])
     engine_add_input(&en_bfd_chassis, &en_sb_chassis, NULL);
     engine_add_input(&en_bfd_chassis, &en_sb_ha_chassis_group, NULL);
 
+    engine_add_input(&en_nat_addresses, &en_ovs_open_vswitch, NULL);
+    engine_add_input(&en_nat_addresses, &en_ovs_bridge, NULL);
+    engine_add_input(&en_nat_addresses, &en_sb_chassis, NULL);
+    engine_add_input(&en_nat_addresses, &en_sb_port_binding, nat_addresses_change_handler);
+    engine_add_input(&en_nat_addresses, &en_runtime_data, nat_addresses_change_handler);
+
     /* Note: The order of inputs is important, all OVS interface changes must
      * be handled before any ct_zone changes.
      */
@@ -5277,6 +5604,8 @@  main(int argc, char *argv[])
                      controller_output_mac_cache_handler);
     engine_add_input(&en_controller_output, &en_bfd_chassis,
                      controller_output_bfd_chassis_handler);
+    engine_add_input(&en_controller_output, &en_nat_addresses,
+                     controller_output_nat_addresses_handler);
 
     struct engine_arg engine_arg = {
         .sb_idl = ovnsb_idl_loop.idl,
@@ -5323,6 +5652,8 @@  main(int argc, char *argv[])
         engine_get_internal_data(&en_ct_zones);
     struct ed_type_bfd_chassis *bfd_chassis_data =
         engine_get_internal_data(&en_bfd_chassis);
+    struct ed_type_nat_addresses *nat_addresses_data =
+        engine_get_internal_data(&en_nat_addresses);
     struct ed_type_runtime_data *runtime_data =
         engine_get_internal_data(&en_runtime_data);
     struct ed_type_template_vars *template_vars_data =
@@ -5656,6 +5987,7 @@  main(int argc, char *argv[])
                     }
 
                     runtime_data = engine_get_data(&en_runtime_data);
+                    nat_addresses_data = engine_get_data(&en_nat_addresses);
                     if (runtime_data) {
                         stopwatch_start(PATCH_RUN_STOPWATCH_NAME, time_msec());
                         patch_run(ovs_idl_txn,
@@ -5703,7 +6035,6 @@  main(int argc, char *argv[])
                         pinctrl_update(ovnsb_idl_loop.idl);
                         pinctrl_run(ovnsb_idl_txn,
                                     sbrec_datapath_binding_by_key,
-                                    sbrec_port_binding_by_datapath,
                                     sbrec_port_binding_by_key,
                                     sbrec_port_binding_by_name,
                                     sbrec_mac_binding_by_lport_ip,
@@ -5718,13 +6049,14 @@  main(int argc, char *argv[])
                                     sbrec_mac_binding_table_get(
                                         ovnsb_idl_loop.idl),
                                     sbrec_bfd_table_get(ovnsb_idl_loop.idl),
-                                    br_int, chassis,
+                                    chassis,
                                     &runtime_data->local_datapaths,
                                     &runtime_data->active_tunnels,
                                     &runtime_data->local_active_ports_ipv6_pd,
                                     &runtime_data->local_active_ports_ras,
                                     ovsrec_open_vswitch_table_get(
-                                            ovs_idl_loop.idl));
+                                            ovs_idl_loop.idl),
+                                    nat_addresses_data);
                         stopwatch_stop(PINCTRL_RUN_STOPWATCH_NAME,
                                        time_msec());
                         mirror_run(ovs_idl_txn,
diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index b891435c1..a5c250dd9 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -233,14 +233,11 @@  static void destroy_send_garps_rarps(void);
 static void send_garp_rarp_wait(long long int send_garp_rarp_time);
 static void send_garp_rarp_prepare(
     struct ovsdb_idl_txn *ovnsb_idl_txn,
-    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
     struct ovsdb_idl_index *sbrec_port_binding_by_name,
     struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
-    const struct ovsrec_bridge *,
-    const struct sbrec_chassis *,
     const struct hmap *local_datapaths,
-    const struct sset *active_tunnels,
-    const struct ovsrec_open_vswitch_table *ovs_table)
+    const struct ovsrec_open_vswitch_table *ovs_table,
+    struct ed_type_nat_addresses *nat_addresses)
     OVS_REQUIRES(pinctrl_mutex);
 static void send_garp_rarp_run(struct rconn *swconn,
                                long long int *send_garp_rarp_time)
@@ -4134,7 +4131,6 @@  pinctrl_update(const struct ovsdb_idl *idl)
 void
 pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
             struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
-            struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
             struct ovsdb_idl_index *sbrec_port_binding_by_key,
             struct ovsdb_idl_index *sbrec_port_binding_by_name,
             struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
@@ -4146,13 +4142,13 @@  pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
             const struct sbrec_service_monitor_table *svc_mon_table,
             const struct sbrec_mac_binding_table *mac_binding_table,
             const struct sbrec_bfd_table *bfd_table,
-            const struct ovsrec_bridge *br_int,
             const struct sbrec_chassis *chassis,
             const struct hmap *local_datapaths,
             const struct sset *active_tunnels,
             const struct shash *local_active_ports_ipv6_pd,
             const struct shash *local_active_ports_ras,
-            const struct ovsrec_open_vswitch_table *ovs_table)
+            const struct ovsrec_open_vswitch_table *ovs_table,
+            struct ed_type_nat_addresses *nat_addresses)
 {
     ovs_mutex_lock(&pinctrl_mutex);
     run_put_mac_bindings(ovnsb_idl_txn, sbrec_datapath_binding_by_key,
@@ -4160,10 +4156,11 @@  pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
                          sbrec_mac_binding_by_lport_ip);
     run_put_vport_bindings(ovnsb_idl_txn, sbrec_datapath_binding_by_key,
                            sbrec_port_binding_by_key, chassis);
-    send_garp_rarp_prepare(ovnsb_idl_txn, sbrec_port_binding_by_datapath,
+    send_garp_rarp_prepare(ovnsb_idl_txn,
                            sbrec_port_binding_by_name,
-                           sbrec_mac_binding_by_lport_ip, br_int, chassis,
-                           local_datapaths, active_tunnels, ovs_table);
+                           sbrec_mac_binding_by_lport_ip,
+                           local_datapaths,
+                           ovs_table, nat_addresses);
     prepare_ipv6_ras(local_active_ports_ras, sbrec_port_binding_by_name);
     prepare_ipv6_prefixd(ovnsb_idl_txn, sbrec_port_binding_by_name,
                          local_active_ports_ipv6_pd, chassis,
@@ -6289,236 +6286,6 @@  ip_mcast_querier_wait(long long int query_time)
     }
 }
 
-/* Get localnet vifs, local l3gw ports and ofport for localnet patch ports. */
-static void
-get_localnet_vifs_l3gwports(
-    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
-    struct ovsdb_idl_index *sbrec_port_binding_by_name,
-    const struct ovsrec_bridge *br_int,
-    const struct sbrec_chassis *chassis,
-    const struct hmap *local_datapaths,
-    struct sset *localnet_vifs,
-    struct sset *local_l3gw_ports)
-{
-    for (int i = 0; i < br_int->n_ports; i++) {
-        const struct ovsrec_port *port_rec = br_int->ports[i];
-        if (!strcmp(port_rec->name, br_int->name)) {
-            continue;
-        }
-        const char *tunnel_id = smap_get(&port_rec->external_ids,
-                                          "ovn-chassis-id");
-        if (tunnel_id &&
-                encaps_tunnel_id_match(tunnel_id, chassis->name, NULL, NULL)) {
-            continue;
-        }
-        const char *localnet = smap_get(&port_rec->external_ids,
-                                        "ovn-localnet-port");
-        if (localnet) {
-            continue;
-        }
-        for (int j = 0; j < port_rec->n_interfaces; j++) {
-            const struct ovsrec_interface *iface_rec = port_rec->interfaces[j];
-            if (!iface_rec->n_ofport) {
-                continue;
-            }
-            /* Get localnet vif. */
-            const char *iface_id = smap_get(&iface_rec->external_ids,
-                                            "iface-id");
-            if (!iface_id) {
-                continue;
-            }
-            const struct sbrec_port_binding *pb
-                = lport_lookup_by_name(sbrec_port_binding_by_name, iface_id);
-            if (!pb || pb->chassis != chassis) {
-                continue;
-            }
-            if (!iface_rec->link_state ||
-                    strcmp(iface_rec->link_state, "up")) {
-                continue;
-            }
-            struct local_datapath *ld
-                = get_local_datapath(local_datapaths,
-                                     pb->datapath->tunnel_key);
-            if (ld && ld->localnet_port) {
-                sset_add(localnet_vifs, iface_id);
-            }
-        }
-    }
-
-    struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
-        sbrec_port_binding_by_datapath);
-
-    const struct local_datapath *ld;
-    HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
-        const struct sbrec_port_binding *pb;
-
-        if (!ld->localnet_port) {
-            continue;
-        }
-
-        /* Get l3gw ports.  Consider port bindings with type "l3gateway"
-         * that connect to gateway routers (if local), and consider port
-         * bindings of type "patch" since they might connect to
-         * distributed gateway ports with NAT addresses. */
-
-        sbrec_port_binding_index_set_datapath(target, ld->datapath);
-        SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
-                                           sbrec_port_binding_by_datapath) {
-            if ((!strcmp(pb->type, "l3gateway") && pb->chassis == chassis)
-                || !strcmp(pb->type, "patch")) {
-                sset_add(local_l3gw_ports, pb->logical_port);
-            }
-        }
-    }
-    sbrec_port_binding_index_destroy_row(target);
-}
-
-
-/* Extracts the mac, IPv4 and IPv6 addresses, and logical port from
- * 'addresses' which should be of the format 'MAC [IP1 IP2 ..]
- * [is_chassis_resident("LPORT_NAME")]', where IPn should be a valid IPv4
- * or IPv6 address, and stores them in the 'ipv4_addrs' and 'ipv6_addrs'
- * fields of 'laddrs'.  The logical port name is stored in 'lport'.
- *
- * Returns true if at least 'MAC' is found in 'address', false otherwise.
- *
- * The caller must call destroy_lport_addresses() and free(*lport). */
-static bool
-extract_addresses_with_port(const char *addresses,
-                            struct lport_addresses *laddrs,
-                            char **lport)
-{
-    int ofs;
-    if (!extract_addresses(addresses, laddrs, &ofs)) {
-        return false;
-    } else if (!addresses[ofs]) {
-        return true;
-    }
-
-    struct lexer lexer;
-    lexer_init(&lexer, addresses + ofs);
-    lexer_get(&lexer);
-
-    if (lexer.error || lexer.token.type != LEX_T_ID
-        || !lexer_match_id(&lexer, "is_chassis_resident")) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-        VLOG_INFO_RL(&rl, "invalid syntax '%s' in address", addresses);
-        lexer_destroy(&lexer);
-        return true;
-    }
-
-    if (!lexer_match(&lexer, LEX_T_LPAREN)) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-        VLOG_INFO_RL(&rl, "Syntax error: expecting '(' after "
-                          "'is_chassis_resident' in address '%s'", addresses);
-        lexer_destroy(&lexer);
-        return false;
-    }
-
-    if (lexer.token.type != LEX_T_STRING) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-        VLOG_INFO_RL(&rl,
-                    "Syntax error: expecting quoted string after "
-                    "'is_chassis_resident' in address '%s'", addresses);
-        lexer_destroy(&lexer);
-        return false;
-    }
-
-    *lport = xstrdup(lexer.token.s);
-
-    lexer_get(&lexer);
-    if (!lexer_match(&lexer, LEX_T_RPAREN)) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-        VLOG_INFO_RL(&rl, "Syntax error: expecting ')' after quoted string in "
-                          "'is_chassis_resident()' in address '%s'",
-                          addresses);
-        lexer_destroy(&lexer);
-        return false;
-    }
-
-    lexer_destroy(&lexer);
-    return true;
-}
-
-static void
-consider_nat_address(struct ovsdb_idl_index *sbrec_port_binding_by_name,
-                     const char *nat_address,
-                     const struct sbrec_port_binding *pb,
-                     struct sset *nat_address_keys,
-                     const struct sbrec_chassis *chassis,
-                     const struct sset *active_tunnels,
-                     struct shash *nat_addresses)
-{
-    struct lport_addresses *laddrs = xmalloc(sizeof *laddrs);
-    char *lport = NULL;
-    if (!extract_addresses_with_port(nat_address, laddrs, &lport)
-        || (!lport && !strcmp(pb->type, "patch"))
-        || (lport && !lport_is_chassis_resident(
-                sbrec_port_binding_by_name, chassis,
-                active_tunnels, lport))) {
-        destroy_lport_addresses(laddrs);
-        free(laddrs);
-        free(lport);
-        return;
-    }
-    free(lport);
-
-    int i;
-    for (i = 0; i < laddrs->n_ipv4_addrs; i++) {
-        char *name = xasprintf("%s-%s", pb->logical_port,
-                                        laddrs->ipv4_addrs[i].addr_s);
-        sset_add(nat_address_keys, name);
-        free(name);
-    }
-    if (laddrs->n_ipv4_addrs == 0) {
-        char *name = xasprintf("%s-noip", pb->logical_port);
-        sset_add(nat_address_keys, name);
-        free(name);
-    }
-    shash_add(nat_addresses, pb->logical_port, laddrs);
-}
-
-static void
-get_nat_addresses_and_keys(struct ovsdb_idl_index *sbrec_port_binding_by_name,
-                           struct sset *nat_address_keys,
-                           struct sset *local_l3gw_ports,
-                           const struct sbrec_chassis *chassis,
-                           const struct sset *active_tunnels,
-                           struct shash *nat_addresses)
-{
-    const char *gw_port;
-    SSET_FOR_EACH(gw_port, local_l3gw_ports) {
-        const struct sbrec_port_binding *pb;
-
-        pb = lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
-        if (!pb) {
-            continue;
-        }
-
-        if (pb->n_nat_addresses) {
-            for (int i = 0; i < pb->n_nat_addresses; i++) {
-                consider_nat_address(sbrec_port_binding_by_name,
-                                     pb->nat_addresses[i], pb,
-                                     nat_address_keys, chassis,
-                                     active_tunnels,
-                                     nat_addresses);
-            }
-        } else {
-            /* Continue to support options:nat-addresses for version
-             * upgrade. */
-            const char *nat_addresses_options = smap_get(&pb->options,
-                                                         "nat-addresses");
-            if (nat_addresses_options) {
-                consider_nat_address(sbrec_port_binding_by_name,
-                                     nat_addresses_options, pb,
-                                     nat_address_keys, chassis,
-                                     active_tunnels,
-                                     nat_addresses);
-            }
-        }
-    }
-}
-
 static void
 send_garp_rarp_wait(long long int send_garp_rarp_time)
 {
@@ -6555,96 +6322,65 @@  send_garp_rarp_run(struct rconn *swconn, long long int *send_garp_rarp_time)
  * thread context. */
 static void
 send_garp_rarp_prepare(struct ovsdb_idl_txn *ovnsb_idl_txn,
-                       struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
                        struct ovsdb_idl_index *sbrec_port_binding_by_name,
                        struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
-                       const struct ovsrec_bridge *br_int,
-                       const struct sbrec_chassis *chassis,
                        const struct hmap *local_datapaths,
-                       const struct sset *active_tunnels,
-                       const struct ovsrec_open_vswitch_table *ovs_table)
+                       const struct ovsrec_open_vswitch_table *ovs_table,
+                       struct ed_type_nat_addresses *nat_addresses)
     OVS_REQUIRES(pinctrl_mutex)
 {
-    struct sset localnet_vifs = SSET_INITIALIZER(&localnet_vifs);
-    struct sset local_l3gw_ports = SSET_INITIALIZER(&local_l3gw_ports);
-    struct sset nat_ip_keys = SSET_INITIALIZER(&nat_ip_keys);
-    struct shash nat_addresses;
-    unsigned long long garp_max_timeout = GARP_RARP_DEF_MAX_TIMEOUT;
-    bool garp_continuous = false;
-    const struct ovsrec_open_vswitch *cfg =
-        ovsrec_open_vswitch_table_first(ovs_table);
-    if (cfg) {
-        garp_max_timeout = smap_get_ullong(
-                &cfg->external_ids, "garp-max-timeout-sec", 0) * 1000;
-        garp_continuous = !!garp_max_timeout;
-        if (!garp_max_timeout) {
-            garp_max_timeout = GARP_RARP_DEF_MAX_TIMEOUT;
-        }
-    }
-
-    shash_init(&nat_addresses);
-
-    get_localnet_vifs_l3gwports(sbrec_port_binding_by_datapath,
-                                sbrec_port_binding_by_name,
-                                br_int, chassis, local_datapaths,
-                                &localnet_vifs, &local_l3gw_ports);
-
-    get_nat_addresses_and_keys(sbrec_port_binding_by_name,
-                               &nat_ip_keys, &local_l3gw_ports,
-                               chassis, active_tunnels,
-                               &nat_addresses);
-    /* For deleted ports and deleted nat ips, remove from
-     * send_garp_rarp_data. */
-    struct shash_node *iter;
-    SHASH_FOR_EACH_SAFE (iter, &send_garp_rarp_data) {
-        if (!sset_contains(&localnet_vifs, iter->name) &&
-            !sset_contains(&nat_ip_keys, iter->name)) {
-            send_garp_rarp_delete(iter->name);
+    if (nat_addresses) {
+        unsigned long long garp_max_timeout = GARP_RARP_DEF_MAX_TIMEOUT;
+        bool garp_continuous = false;
+
+        const struct ovsrec_open_vswitch *cfg =
+            ovsrec_open_vswitch_table_first(ovs_table);
+        if (cfg) {
+            garp_max_timeout = smap_get_ullong(
+                    &cfg->external_ids, "garp-max-timeout-sec", 0) * 1000;
+            garp_continuous = !!garp_max_timeout;
+            if (!garp_max_timeout) {
+                garp_max_timeout = GARP_RARP_DEF_MAX_TIMEOUT;
+            }
         }
-    }
-
-    /* Update send_garp_rarp_data. */
-    const char *iface_id;
-    SSET_FOR_EACH (iface_id, &localnet_vifs) {
-        const struct sbrec_port_binding *pb = lport_lookup_by_name(
-            sbrec_port_binding_by_name, iface_id);
-        if (pb && !smap_get_bool(&pb->options, "disable_garp_rarp", false)) {
-            send_garp_rarp_update(ovnsb_idl_txn,
-                                  sbrec_mac_binding_by_lport_ip,
-                                  local_datapaths, pb, &nat_addresses,
-                                  garp_max_timeout, garp_continuous);
+        /* For deleted ports and deleted nat ips, remove from
+        * send_garp_rarp_data. */
+        struct shash_node *iter;
+        SHASH_FOR_EACH_SAFE (iter, &send_garp_rarp_data) {
+            if (!sset_contains(&nat_addresses->localnet_vifs, iter->name) &&
+                !sset_contains(&nat_addresses->nat_ip_keys, iter->name)) {
+                send_garp_rarp_delete(iter->name);
+            }
         }
-    }
-
-    /* Update send_garp_rarp_data for nat-addresses. */
-    const char *gw_port;
-    SSET_FOR_EACH (gw_port, &local_l3gw_ports) {
-        const struct sbrec_port_binding *pb
-            = lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
-        if (pb && !smap_get_bool(&pb->options, "disable_garp_rarp", false)) {
-            send_garp_rarp_update(ovnsb_idl_txn, sbrec_mac_binding_by_lport_ip,
-                                  local_datapaths, pb, &nat_addresses,
-                                  garp_max_timeout, garp_continuous);
+        /* Update send_garp_rarp_data. */
+        const char *iface_id;
+        SSET_FOR_EACH (iface_id, &nat_addresses->localnet_vifs) {
+            const struct sbrec_port_binding *pb = lport_lookup_by_name(
+                sbrec_port_binding_by_name, iface_id);
+            if (pb && !smap_get_bool(&pb->options, "disable_garp_rarp", false)) {
+                send_garp_rarp_update(ovnsb_idl_txn,
+                                    sbrec_mac_binding_by_lport_ip,
+                                    local_datapaths, pb, &nat_addresses->nat_addresses,
+                                    garp_max_timeout, garp_continuous);
+            }
         }
-    }
 
-    /* pinctrl_handler thread will send the GARPs. */
-
-    sset_destroy(&localnet_vifs);
-    sset_destroy(&local_l3gw_ports);
+        /* Update send_garp_rarp_data for nat-addresses. */
+        const char *gw_port;
+        SSET_FOR_EACH (gw_port, &nat_addresses->local_l3gw_ports) {
+            const struct sbrec_port_binding *pb
+                = lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
+            if (pb && !smap_get_bool(&pb->options, "disable_garp_rarp", false)) {
+                send_garp_rarp_update(ovnsb_idl_txn, sbrec_mac_binding_by_lport_ip,
+                                    local_datapaths, pb, &nat_addresses->nat_addresses,
+                                    garp_max_timeout, garp_continuous);
+            }
+        }
 
-    SHASH_FOR_EACH_SAFE (iter, &nat_addresses) {
-        struct lport_addresses *laddrs = iter->data;
-        destroy_lport_addresses(laddrs);
-        shash_delete(&nat_addresses, iter);
-        free(laddrs);
+        /* pinctrl_handler thread will send the GARPs. */
+        garp_rarp_max_timeout = garp_max_timeout;
+        garp_rarp_continuous = garp_continuous;
     }
-    shash_destroy(&nat_addresses);
-
-    sset_destroy(&nat_ip_keys);
-
-    garp_rarp_max_timeout = garp_max_timeout;
-    garp_rarp_continuous = garp_continuous;
 }
 
 static bool
diff --git a/controller/pinctrl.h b/controller/pinctrl.h
index 3462b670c..cf92bddd4 100644
--- a/controller/pinctrl.h
+++ b/controller/pinctrl.h
@@ -20,6 +20,7 @@ 
 #include <stdint.h>
 
 #include "lib/sset.h"
+#include "openvswitch/shash.h"
 #include "openvswitch/list.h"
 #include "openvswitch/meta-flow.h"
 
@@ -39,10 +40,16 @@  struct sbrec_bfd_table;
 struct sbrec_port_binding;
 struct sbrec_mac_binding_table;
 
+struct ed_type_nat_addresses {
+    struct shash nat_addresses;
+    struct sset localnet_vifs;
+    struct sset local_l3gw_ports;
+    struct sset nat_ip_keys;
+};
+
 void pinctrl_init(void);
 void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
                  struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
-                 struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
                  struct ovsdb_idl_index *sbrec_port_binding_by_key,
                  struct ovsdb_idl_index *sbrec_port_binding_by_name,
                  struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
@@ -54,12 +61,13 @@  void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
                  const struct sbrec_service_monitor_table *,
                  const struct sbrec_mac_binding_table *,
                  const struct sbrec_bfd_table *,
-                 const struct ovsrec_bridge *, const struct sbrec_chassis *,
+                 const struct sbrec_chassis *,
                  const struct hmap *local_datapaths,
                  const struct sset *active_tunnels,
                  const struct shash *local_active_ports_ipv6_pd,
                  const struct shash *local_active_ports_ras,
-                 const struct ovsrec_open_vswitch_table *ovs_table);
+                 const struct ovsrec_open_vswitch_table *ovs_table,
+                 struct ed_type_nat_addresses *nat_addresses);
 void pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn);
 void pinctrl_destroy(void);