diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
index d45c5df..431b61e 100644
--- a/ovn/controller/binding.c
+++ b/ovn/controller/binding.c
@@ -58,6 +58,8 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
     ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
     ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name);
     ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_external_ids);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
     ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_status);
 
     ovsdb_idl_add_table(ovs_idl, &ovsrec_table_qos);
@@ -68,7 +70,8 @@ static void
 get_local_iface_ids(const struct ovsrec_bridge *br_int,
                     struct shash *lport_to_iface,
                     struct sset *local_lports,
-                    struct sset *egress_ifaces)
+                    struct sset *egress_ifaces,
+                    struct sset *active_tunnels)
 {
     int i;
 
@@ -100,6 +103,20 @@ get_local_iface_ids(const struct ovsrec_bridge *br_int,
                 if (tunnel_iface) {
                     sset_add(egress_ifaces, tunnel_iface);
                 }
+                /* Add ovn-chassis-id if the bfd_status of the tunnel
+                 * is active */
+                const char *bfd = smap_get(&iface_rec->bfd, "enable");
+                if (bfd && !strcmp(bfd, "true")) {
+                    const char *status = smap_get(&iface_rec->bfd_status,
+                                                  "state");
+                    if (status && !strcmp(status, "up")) {
+                        const char *id = smap_get(&port_rec->external_ids,
+                                                  "ovn-chassis-id");
+                        if (id) {
+                            sset_add(active_tunnels, id);
+                        }
+                    }
+                }
             }
         }
     }
@@ -110,7 +127,8 @@ add_local_datapath__(const struct ldatapath_index *ldatapaths,
                      const struct lport_index *lports,
                      const struct sbrec_datapath_binding *datapath,
                      bool has_local_l3gateway, int depth,
-                     struct hmap *local_datapaths)
+                     struct hmap *local_datapaths,
+                     bool has_local_redirectchassis)
 {
     uint32_t dp_key = datapath->tunnel_key;
 
@@ -119,6 +137,9 @@ add_local_datapath__(const struct ldatapath_index *ldatapaths,
         if (has_local_l3gateway) {
             ld->has_local_l3gateway = true;
         }
+        if (has_local_redirectchassis) {
+            ld->has_local_redirectchassis = true;
+        }
         return;
     }
 
@@ -129,6 +150,7 @@ add_local_datapath__(const struct ldatapath_index *ldatapaths,
     ovs_assert(ld->ldatapath);
     ld->localnet_port = NULL;
     ld->has_local_l3gateway = has_local_l3gateway;
+    ld->has_local_redirectchassis = has_local_redirectchassis;
 
     if (depth >= 100) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
@@ -146,7 +168,14 @@ add_local_datapath__(const struct ldatapath_index *ldatapaths,
                     lports, peer_name);
                 if (peer && peer->datapath) {
                     add_local_datapath__(ldatapaths, lports, peer->datapath,
-                                         false, depth + 1, local_datapaths);
+                                         false, depth + 1, local_datapaths,
+                                         false);
+                    ld->n_peer_dps++;
+                    ld->peer_dps = xrealloc(
+                            ld->peer_dps,
+                            ld->n_peer_dps * sizeof *ld->peer_dps);
+                    ld->peer_dps[ld->n_peer_dps - 1] = ldatapath_lookup_by_key(
+                        ldatapaths, peer->datapath->tunnel_key);
                 }
             }
         }
@@ -157,10 +186,11 @@ static void
 add_local_datapath(const struct ldatapath_index *ldatapaths,
                    const struct lport_index *lports,
                    const struct sbrec_datapath_binding *datapath,
-                   bool has_local_l3gateway, struct hmap *local_datapaths)
+                   bool has_local_l3gateway, struct hmap *local_datapaths,
+                   bool has_local_redirectchassis)
 {
     add_local_datapath__(ldatapaths, lports, datapath, has_local_l3gateway, 0,
-                         local_datapaths);
+                         local_datapaths, has_local_redirectchassis);
 }
 
 static void
@@ -362,10 +392,12 @@ consider_local_datapath(struct controller_ctx *ctx,
                         struct hmap *qos_map,
                         struct hmap *local_datapaths,
                         struct shash *lport_to_iface,
-                        struct sset *local_lports)
+                        struct sset *local_lports,
+                        struct sset *active_tunnels)
 {
     const struct ovsrec_interface *iface_rec
         = shash_find_data(lport_to_iface, binding_rec->logical_port);
+    struct ovs_list *redirect_chassis = NULL;
 
     bool our_chassis = false;
     if (iface_rec
@@ -376,7 +408,7 @@ consider_local_datapath(struct controller_ctx *ctx,
             sset_add(local_lports, binding_rec->logical_port);
         }
         add_local_datapath(ldatapaths, lports, binding_rec->datapath,
-                           false, local_datapaths);
+                           false, local_datapaths, false);
         if (iface_rec && qos_map && ctx->ovs_idl_txn) {
             get_qos_params(binding_rec, qos_map);
         }
@@ -391,24 +423,35 @@ consider_local_datapath(struct controller_ctx *ctx,
         if (our_chassis) {
             sset_add(local_lports, binding_rec->logical_port);
             add_local_datapath(ldatapaths, lports, binding_rec->datapath,
-                               false, local_datapaths);
+                               false, local_datapaths, false);
         }
     } else if (!strcmp(binding_rec->type, "chassisredirect")) {
-        if (pb_redirect_chassis_contains(binding_rec, chassis_rec)) {
+        redirect_chassis = parse_redirect_chassis(binding_rec);
+
+        if (redirect_chassis &&
+           redirect_chassis_contains(redirect_chassis, chassis_rec)) {
+            struct redirect_chassis *rc;
+            LIST_FOR_EACH (rc, node, redirect_chassis) {
+                if (!strcmp(rc->chassis_id, chassis_rec->name)) {
+                    /* sb_rec_port_binding->chassis should reflect master */
+                    our_chassis = true;
+                    break;
+                }
+                if (sset_contains(active_tunnels, rc->chassis_id)) {
+                    break;
+                }
+            }
             add_local_datapath(ldatapaths, lports, binding_rec->datapath,
-                               false, local_datapaths);
-            // XXX this should only be set to true if our chassis
-            // (chassis_rec) is the master for this chassisredirect port
-            // but for now we'll bind it only when not bound
-            our_chassis = !binding_rec->chassis;
+                               false, local_datapaths, our_chassis);
         }
+        redirect_chassis_destroy(redirect_chassis);
     } else if (!strcmp(binding_rec->type, "l3gateway")) {
         const char *chassis_id = smap_get(&binding_rec->options,
                                           "l3gateway-chassis");
         our_chassis = chassis_id && !strcmp(chassis_id, chassis_rec->name);
         if (our_chassis) {
             add_local_datapath(ldatapaths, lports, binding_rec->datapath,
-                               true, local_datapaths);
+                               true, local_datapaths, false);
         }
     } else if (!strcmp(binding_rec->type, "localnet")) {
         /* Add all localnet ports to local_lports so that we allocate ct zones
@@ -457,12 +500,13 @@ binding_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
     const struct sbrec_port_binding *binding_rec;
     struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface);
     struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces);
+    struct sset active_tunnels = SSET_INITIALIZER(&active_tunnels);
     struct hmap qos_map;
 
     hmap_init(&qos_map);
     if (br_int) {
         get_local_iface_ids(br_int, &lport_to_iface, local_lports,
-                            &egress_ifaces);
+                            &egress_ifaces, &active_tunnels);
     }
 
     /* Run through each binding record to see if it is resident on this
@@ -473,10 +517,9 @@ binding_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
                                 chassis_rec, binding_rec,
                                 sset_is_empty(&egress_ifaces) ? NULL :
                                 &qos_map, local_datapaths, &lport_to_iface,
-                                local_lports);
+                                local_lports, &active_tunnels);
 
     }
-
     if (!sset_is_empty(&egress_ifaces)
         && set_noop_qos(ctx, &egress_ifaces)) {
         const char *entry;
@@ -487,9 +530,132 @@ binding_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
 
     shash_destroy(&lport_to_iface);
     sset_destroy(&egress_ifaces);
+    sset_destroy(&active_tunnels);
     hmap_destroy(&qos_map);
 }
 
+
+static void
+calculate_bfd_chassis(struct sset *bfd_chassis,
+                      const struct sbrec_chassis *our_chassis,
+                      struct hmap *local_datapaths)
+{
+    /* Identify all chassis nodes to which we need to enable bfd.
+     * 1) Any chassis hosting the chassisredirect ports for known
+     *    router datapaths.
+     * 2) Chassis hosting peer datapaths (with ports) connected
+     *    to a router datapath  when our chassis is hosting a router
+     *    with a chassis redirect port. */
+    struct local_datapath *dp;
+    HMAP_FOR_EACH (dp, hmap_node, local_datapaths) {
+        const char *is_router = smap_get(&dp->datapath->external_ids,
+                                         "logical-router");
+        bool our_chassis_is_gw_for_dp = false;
+        if (is_router) {
+            for (size_t j = 0; j < dp->ldatapath->n_lports; j++) {
+                const struct sbrec_port_binding *pb = dp->ldatapath->lports[j];
+                if (!strcmp(pb->type, "chassisredirect")) {
+                    struct ovs_list *redirect_chassis = NULL;
+                    redirect_chassis = parse_redirect_chassis(pb);
+                    /* we don't need BFD for non-HA  chassisredirect */
+                    if (!redirect_chassis ||
+                        ovs_list_is_short(redirect_chassis)) {
+                        continue;
+                    }
+                    our_chassis_is_gw_for_dp = redirect_chassis_contains(
+                            redirect_chassis, our_chassis);
+                    struct redirect_chassis *chassis;
+                    LIST_FOR_EACH (chassis, node, redirect_chassis) {
+                        sset_add(bfd_chassis, chassis->chassis_id);
+                    }
+                    redirect_chassis_destroy(redirect_chassis);
+                    break;
+                }
+            }
+        }
+        if (our_chassis_is_gw_for_dp) {
+            for (size_t i = 0; i < dp->n_peer_dps; i++) {
+                const struct ldatapath *pdp = dp->peer_dps[i];
+                if (!pdp) {
+                    continue;
+                }
+                for (size_t j = 0; j < pdp->n_lports; j++) {
+                    const struct sbrec_port_binding *pb = pdp->lports[j];
+                    const char *chassis_name = NULL;
+                    if (!pb->chassis) {
+                        continue;
+                    }
+                    /* Gateway node has to enable bfd to all nodes hosting
+                     * connected network ports */
+                    chassis_name = pb->chassis->name;
+                    if (chassis_name) {
+                        sset_add(bfd_chassis, chassis_name);
+                    }
+                }
+            }
+        }
+    }
+}
+
+void
+bfd_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
+        const struct sbrec_chassis *chassis_rec,
+        struct hmap *local_datapaths)
+{
+    if (!chassis_rec) {
+        return;
+    }
+    struct sset bfd_chassis = SSET_INITIALIZER(&bfd_chassis);
+    /* Identify all chassis nodes to which we need to enable bfd */
+    calculate_bfd_chassis(&bfd_chassis, chassis_rec, local_datapaths);
+
+    /* Identify tunnels ports(connected to remote chassis id) to enable bfd */
+    struct sset tunnels = SSET_INITIALIZER(&tunnels);
+    struct sset bfd_ifaces = SSET_INITIALIZER(&bfd_ifaces);
+    for (size_t k = 0; k < br_int->n_ports; k++) {
+        const char *chassis_id = smap_get(&br_int->ports[k]->external_ids,
+                                          "ovn-chassis-id");
+        if (chassis_id) {
+            char *port_name = br_int->ports[k]->name;
+            sset_add(&tunnels, port_name);
+            if (sset_contains(&bfd_chassis, chassis_id)) {
+                sset_add(&bfd_ifaces, port_name);
+            }
+        }
+    }
+
+    /* Enable or disable bfd */
+    const struct ovsrec_interface *iface;
+    struct smap bfd = SMAP_INITIALIZER(&bfd);
+    OVSREC_INTERFACE_FOR_EACH (iface, ctx->ovs_idl) {
+        if (sset_contains(&tunnels, iface->name)) {
+            const char *bfd_enabled = smap_get(&iface->bfd, "enable");
+            if (sset_contains(&bfd_ifaces, iface->name)) {
+                smap_add(&bfd, "enable", "true");
+                if (bfd_enabled && !strcmp(bfd_enabled, "true")) {
+                    /* If already set to true we skip setting it again
+                     * to avoid flapping to bfd initialization state */
+                    continue;
+                }
+                ovsrec_interface_verify_bfd(iface);
+                ovsrec_interface_set_bfd(iface, &bfd);
+            } else {
+                smap_add(&bfd, "enable", "false");
+                if (bfd_enabled && !strcmp(bfd_enabled, "false")) {
+                    /* If already set to false we skip setting it again */
+                    continue;
+                }
+                ovsrec_interface_verify_bfd(iface);
+                ovsrec_interface_set_bfd(iface, &bfd);
+            }
+        }
+    }
+    sset_destroy(&bfd_chassis);
+    sset_destroy(&tunnels);
+    sset_destroy(&bfd_ifaces);
+    smap_destroy(&bfd);
+}
+
 /* Returns true if the database is all cleaned up, false if more work is
  * required. */
 bool
diff --git a/ovn/controller/binding.h b/ovn/controller/binding.h
index 3bfa7d1..2b88ec5 100644
--- a/ovn/controller/binding.h
+++ b/ovn/controller/binding.h
@@ -33,6 +33,9 @@ void binding_run(struct controller_ctx *, const struct ovsrec_bridge *br_int,
                  const struct sbrec_chassis *, const struct ldatapath_index *,
                  const struct lport_index *, struct hmap *local_datapaths,
                  struct sset *all_lports);
+void bfd_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
+             const struct sbrec_chassis *chassis_rec,
+             struct hmap *local_datapaths);
 bool binding_cleanup(struct controller_ctx *, const struct sbrec_chassis *);
 
 #endif /* ovn/binding.h */
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 21893bc..7930b6e 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -532,6 +532,8 @@ main(int argc, char *argv[])
     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_open_vswitch_col_bridges);
     ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_interface);
     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_name);
+    ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_bfd);
+    ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_bfd_status);
     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_type);
     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_options);
     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_ofport);
@@ -651,6 +653,7 @@ main(int argc, char *argv[])
                               &local_datapaths, &group_table,
                               &addr_sets, &flow_table);
 
+                    bfd_run(&ctx, br_int, chassis, &local_datapaths);
                     physical_run(&ctx, mff_ovn_geneve,
                                  br_int, chassis, &ct_zones, &lports,
                                  &flow_table, &local_datapaths, &local_lports);
@@ -705,6 +708,7 @@ main(int argc, char *argv[])
 
         struct local_datapath *cur_node, *next_node;
         HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, &local_datapaths) {
+            free(cur_node->peer_dps);
             hmap_remove(&local_datapaths, &cur_node->hmap_node);
             free(cur_node);
         }
diff --git a/ovn/controller/ovn-controller.h b/ovn/controller/ovn-controller.h
index 4bc0467..72dfce5 100644
--- a/ovn/controller/ovn-controller.h
+++ b/ovn/controller/ovn-controller.h
@@ -66,6 +66,9 @@ struct local_datapath {
     /* True if this datapath contains an l3gateway port located on this
      * hypervisor. */
     bool has_local_l3gateway;
+    bool has_local_redirectchassis;
+    const struct ldatapath **peer_dps;
+    size_t n_peer_dps;
 };
 
 struct local_datapath *get_local_datapath(const struct hmap *,
