diff --git a/NEWS b/NEWS
index 4d6ebea89..4979bb806 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,12 @@ Post v24.03.0
     flow table id.
     "lflow-stage-to-oftable STAGE_NAME" that converts stage name into OpenFlow
     table id.
+  - Add option to ovn-controller called "--br-int-remote=REMOTE" that allows
+    to specify connection method to integration bridge for ovn-controller,
+    defaulting to the unix socket.
+  - Add option to ovn-controller called "--br-int-probe-interval=INTERVAL"
+    that sets probe interval for integration bridge connection,
+    disabled by default.
 
 OVN v24.03.0 - 01 Mar 2024
 --------------------------
diff --git a/controller/ofctrl.c b/controller/ofctrl.c
index 6ca2ea4ce..83532bc96 100644
--- a/controller/ofctrl.c
+++ b/controller/ofctrl.c
@@ -772,19 +772,13 @@ ofctrl_get_mf_field_id(void)
  * Returns 'true' if an OpenFlow reconnect happened; 'false' otherwise.
  */
 bool
-ofctrl_run(const struct ovsrec_bridge *br_int,
+ofctrl_run(const char *conn_target, int probe_interval,
            const struct ovsrec_open_vswitch_table *ovs_table,
            struct shash *pending_ct_zones)
 {
-    char *target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int->name);
     bool reconnected = false;
 
-    if (strcmp(target, rconn_get_target(swconn))) {
-        VLOG_INFO("%s: connecting to switch", target);
-        rconn_connect(swconn, target, target);
-    }
-    free(target);
-
+    ovn_update_swconn_at(swconn, conn_target, probe_interval, "ofctrl");
     rconn_run(swconn);
 
     if (!rconn_is_connected(swconn) || !pending_ct_zones) {
diff --git a/controller/ofctrl.h b/controller/ofctrl.h
index 502c73da6..7df0a24ea 100644
--- a/controller/ofctrl.h
+++ b/controller/ofctrl.h
@@ -50,8 +50,9 @@ struct ovn_desired_flow_table {
 /* Interface for OVN main loop. */
 void ofctrl_init(struct ovn_extend_table *group_table,
                  struct ovn_extend_table *meter_table);
-bool ofctrl_run(const struct ovsrec_bridge *br_int,
-                const struct ovsrec_open_vswitch_table *,
+
+bool ofctrl_run(const char *conn_target, int probe_interval,
+                const struct ovsrec_open_vswitch_table *ovs_table,
                 struct shash *pending_ct_zones);
 enum mf_field_id ofctrl_get_mf_field_id(void);
 void ofctrl_put(struct ovn_desired_flow_table *lflow_table,
diff --git a/controller/ovn-controller.8.xml b/controller/ovn-controller.8.xml
index 5ebef048d..a86b41eea 100644
--- a/controller/ovn-controller.8.xml
+++ b/controller/ovn-controller.8.xml
@@ -30,6 +30,18 @@
     </p>
 
     <h1>Options</h1>
+    <dl>
+      <dt><code>--br-int-remote=<var>remote-socket</var></code></dt>
+      <dd>
+        Connection to the br-int management interface. Otherwise, the default
+        is <code>unix:@RUNDIR@/<var>BR_INT_NAME</var>.mgmt</code>.
+      </dd>
+      <dt><code>--br-int-remote-probe-interval=<var>interval</var></code></dt>
+      <dd>
+        Probe interval in seconds, with <code>0</code> meaning disabled.
+        Otherwise, the default is <code>0</code>.
+      </dd>
+    </dl>
 
     <h2>Daemon Options</h2>
     <xi:include href="lib/daemon.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index c9ff5967a..657574fa8 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -122,7 +122,13 @@ static unixctl_cb_func debug_ignore_startup_delay;
 #define OVS_NB_CFG_TS_NAME "ovn-nb-cfg-ts"
 #define OVS_STARTUP_TS_NAME "ovn-startup-ts"
 
-static char *parse_options(int argc, char *argv[]);
+struct br_int_remote {
+    char *target;
+    int probe_interval;
+    bool use_unix_socket;
+};
+
+static char *parse_options(int argc, char *argv[], struct br_int_remote *);
 OVS_NO_RETURN static void usage(void);
 
 /* SSL options */
@@ -5052,11 +5058,30 @@ check_northd_version(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl,
     return true;
 }
 
+static void
+br_int_remote_update(struct br_int_remote *remote,
+                         const struct ovsrec_bridge *br_int)
+{
+    if (!remote->use_unix_socket || !br_int) {
+        return;
+    }
+
+    char *target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int->name);
+    if (!remote->target || strcmp(remote->target, target)) {
+        free(remote->target);
+        remote->target = xstrdup(target);
+    }
+    free(target);
+}
+
 int
 main(int argc, char *argv[])
 {
     struct unixctl_server *unixctl;
     struct ovn_exit_args exit_args = {0};
+    struct br_int_remote br_int_remote = (struct br_int_remote) {
+            .use_unix_socket = true
+    };
     int retval;
 
     /* Read from system-id-override file once on startup. */
@@ -5065,7 +5090,7 @@ main(int argc, char *argv[])
     ovs_cmdl_proctitle_init(argc, argv);
     ovn_set_program_name(argv[0]);
     service_start(&argc, &argv);
-    char *ovs_remote = parse_options(argc, argv);
+    char *ovs_remote = parse_options(argc, argv, &br_int_remote);
     fatal_ignore_sigpipe();
 
     daemonize_start(true, false);
@@ -5666,6 +5691,11 @@ main(int argc, char *argv[])
                        ovsrec_server_has_datapath_table(ovs_idl_loop.idl)
                        ? &br_int_dp
                        : NULL);
+        br_int_remote_update(&br_int_remote, br_int);
+        statctrl_update_swconn(br_int_remote.target,
+                               br_int_remote.probe_interval);
+        pinctrl_update_swconn(br_int_remote.target,
+                              br_int_remote.probe_interval);
 
         /* Enable ACL matching for double tagged traffic. */
         if (ovs_idl_txn) {
@@ -5720,7 +5750,8 @@ main(int argc, char *argv[])
             if (ovs_idl_txn
                 && ovs_feature_support_run(br_int_dp ?
                                            &br_int_dp->capabilities : NULL,
-                                           br_int ? br_int->name : NULL)) {
+                                           br_int_remote.target,
+                                           br_int_remote.probe_interval)) {
                 VLOG_INFO("OVS feature set changed, force recompute.");
                 engine_set_force_recompute(true);
                 if (ovs_feature_set_discovered()) {
@@ -5738,7 +5769,8 @@ main(int argc, char *argv[])
 
             if (br_int) {
                 ct_zones_data = engine_get_data(&en_ct_zones);
-                if (ofctrl_run(br_int, ovs_table,
+                if (ofctrl_run(br_int_remote.target,
+                               br_int_remote.probe_interval, ovs_table,
                                ct_zones_data ? &ct_zones_data->pending
                                              : NULL)) {
                     static struct vlog_rate_limit rl
@@ -5855,7 +5887,7 @@ main(int argc, char *argv[])
                         }
                         stopwatch_start(PINCTRL_RUN_STOPWATCH_NAME,
                                         time_msec());
-                        pinctrl_update(ovnsb_idl_loop.idl, br_int->name);
+                        pinctrl_update(ovnsb_idl_loop.idl);
                         pinctrl_run(ovnsb_idl_txn,
                                     sbrec_datapath_binding_by_key,
                                     sbrec_port_binding_by_datapath,
@@ -5909,7 +5941,6 @@ main(int argc, char *argv[])
                     }
 
                     if (mac_cache_data) {
-                        statctrl_update(br_int->name);
                         statctrl_run(ovnsb_idl_txn, mac_cache_data);
                     }
 
@@ -6030,13 +6061,6 @@ main(int argc, char *argv[])
             binding_wait();
         }
 
-        if (!northd_version_match && br_int) {
-            /* Set the integration bridge name to pinctrl so that the pinctrl
-             * thread can handle any packet-ins when we are not processing
-             * any DB updates due to version mismatch. */
-            pinctrl_set_br_int_name(br_int->name);
-        }
-
         unixctl_server_run(unixctl);
 
         unixctl_server_wait(unixctl);
@@ -6169,6 +6193,7 @@ loop_done:
     ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
 
     ovs_feature_support_destroy();
+    free(br_int_remote.target);
     free(ovs_remote);
     free(file_system_id);
     free(cli_system_id);
@@ -6181,7 +6206,7 @@ loop_done:
 }
 
 static char *
-parse_options(int argc, char *argv[])
+parse_options(int argc, char *argv[], struct br_int_remote *br_int_remote)
 {
     enum {
         OPT_PEER_CA_CERT = UCHAR_MAX + 1,
@@ -6190,6 +6215,8 @@ parse_options(int argc, char *argv[])
         OVN_DAEMON_OPTION_ENUMS,
         SSL_OPTION_ENUMS,
         OPT_ENABLE_DUMMY_VIF_PLUG,
+        BR_INT_REMOTE,
+        BR_INT_PROBE_INTERVAL,
     };
 
     static struct option long_options[] = {
@@ -6203,6 +6230,9 @@ parse_options(int argc, char *argv[])
         {"chassis", required_argument, NULL, 'n'},
         {"enable-dummy-vif-plug", no_argument, NULL,
          OPT_ENABLE_DUMMY_VIF_PLUG},
+        {"br-int-remote", required_argument, NULL, BR_INT_REMOTE},
+        {"br-int-probe-interval", required_argument, NULL,
+         BR_INT_PROBE_INTERVAL},
         {NULL, 0, NULL, 0}
     };
     char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
@@ -6264,6 +6294,16 @@ parse_options(int argc, char *argv[])
             cli_system_id = xstrdup(optarg);
             break;
 
+        case BR_INT_REMOTE:
+            free(br_int_remote->target);
+            br_int_remote->target = xstrdup(optarg);
+            br_int_remote->use_unix_socket = false;
+            break;
+
+        case BR_INT_PROBE_INTERVAL:
+            str_to_int(optarg, 10, &br_int_remote->probe_interval);
+            break;
+
         case '?':
             exit(EXIT_FAILURE);
 
diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index 2d3595cd2..27c47d02b 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -173,7 +173,8 @@ static bool garp_rarp_continuous;
 static void *pinctrl_handler(void *arg);
 
 struct pinctrl {
-    char *br_int_name;
+    /* OpenFlow connection to the switch. */
+    struct rconn *swconn;
     pthread_t pinctrl_thread;
     /* Latch to destroy the 'pinctrl_thread' */
     struct latch pinctrl_thread_exit;
@@ -563,7 +564,7 @@ pinctrl_init(void)
     init_svc_monitors();
     bfd_monitor_init();
     init_fdb_entries();
-    pinctrl.br_int_name = NULL;
+    pinctrl.swconn = rconn_create(0, 0, DSCP_DEFAULT, 1 << OFP15_VERSION);
     pinctrl.mac_binding_can_timestamp = false;
     pinctrl_handler_seq = seq_create();
     pinctrl_main_seq = seq_create();
@@ -3537,30 +3538,13 @@ notify_pinctrl_main(void)
     seq_change(pinctrl_main_seq);
 }
 
-static void
-pinctrl_rconn_setup(struct rconn *swconn, const char *br_int_name)
-    OVS_REQUIRES(pinctrl_mutex)
-{
-    if (br_int_name) {
-        char *target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int_name);
-
-        if (strcmp(target, rconn_get_target(swconn))) {
-            VLOG_INFO("%s: connecting to switch", target);
-            rconn_connect(swconn, target, target);
-        }
-        free(target);
-    } else {
-        rconn_disconnect(swconn);
-    }
-}
-
 /* pinctrl_handler pthread function. */
 static void *
 pinctrl_handler(void *arg_)
 {
     struct pinctrl *pctrl = arg_;
     /* OpenFlow connection to the switch. */
-    struct rconn *swconn;
+    struct rconn *swconn = pctrl->swconn;
     /* Last seen sequence number for 'swconn'.  When this differs from
      * rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */
     unsigned int conn_seq_no = 0;
@@ -3576,13 +3560,10 @@ pinctrl_handler(void *arg_)
     static long long int svc_monitors_next_run_time = LLONG_MAX;
     static long long int send_prefixd_time = LLONG_MAX;
 
-    swconn = rconn_create(0, 0, DSCP_DEFAULT, 1 << OFP15_VERSION);
-
     while (!latch_is_set(&pctrl->pinctrl_thread_exit)) {
         long long int bfd_time = LLONG_MAX;
 
         ovs_mutex_lock(&pinctrl_mutex);
-        pinctrl_rconn_setup(swconn, pctrl->br_int_name);
         ip_mcast_snoop_run();
         ovs_mutex_unlock(&pinctrl_mutex);
 
@@ -3640,37 +3621,24 @@ pinctrl_handler(void *arg_)
         poll_block();
     }
 
-    rconn_destroy(swconn);
     return NULL;
 }
 
-static void
-pinctrl_set_br_int_name_(const char *br_int_name)
-    OVS_REQUIRES(pinctrl_mutex)
+void
+pinctrl_update_swconn(const char *target, int probe_interval)
 {
-    if (br_int_name && (!pinctrl.br_int_name || strcmp(pinctrl.br_int_name,
-                                                       br_int_name))) {
-        free(pinctrl.br_int_name);
-        pinctrl.br_int_name = xstrdup(br_int_name);
-        /* Notify pinctrl_handler that integration bridge is
-         * set/changed. */
+    if (ovn_update_swconn_at(pinctrl.swconn, target,
+                             probe_interval, "pinctrl")) {
+        /* Notify pinctrl_handler that integration bridge
+         * target is set/changed. */
         notify_pinctrl_handler();
     }
 }
 
 void
-pinctrl_set_br_int_name(const char *br_int_name)
-{
-    ovs_mutex_lock(&pinctrl_mutex);
-    pinctrl_set_br_int_name_(br_int_name);
-    ovs_mutex_unlock(&pinctrl_mutex);
-}
-
-void
-pinctrl_update(const struct ovsdb_idl *idl, const char *br_int_name)
+pinctrl_update(const struct ovsdb_idl *idl)
 {
     ovs_mutex_lock(&pinctrl_mutex);
-    pinctrl_set_br_int_name_(br_int_name);
 
     bool can_mb_timestamp =
             sbrec_server_has_mac_binding_table_col_timestamp(idl);
@@ -4292,7 +4260,7 @@ pinctrl_destroy(void)
     latch_set(&pinctrl.pinctrl_thread_exit);
     pthread_join(pinctrl.pinctrl_thread, NULL);
     latch_destroy(&pinctrl.pinctrl_thread_exit);
-    free(pinctrl.br_int_name);
+    rconn_destroy(pinctrl.swconn);
     destroy_send_garps_rarps();
     destroy_ipv6_ras();
     destroy_ipv6_prefixd();
diff --git a/controller/pinctrl.h b/controller/pinctrl.h
index 23343f097..3462b670c 100644
--- a/controller/pinctrl.h
+++ b/controller/pinctrl.h
@@ -62,8 +62,10 @@ void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
                  const struct ovsrec_open_vswitch_table *ovs_table);
 void pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn);
 void pinctrl_destroy(void);
-void pinctrl_set_br_int_name(const char *br_int_name);
-void pinctrl_update(const struct ovsdb_idl *idl, const char *br_int_name);
+
+void pinctrl_update_swconn(const char *target, int probe_interval);
+
+void pinctrl_update(const struct ovsdb_idl *idl);
 
 struct activated_port {
     uint32_t dp_key;
diff --git a/controller/statctrl.c b/controller/statctrl.c
index 8cce97df8..6e6d7d799 100644
--- a/controller/statctrl.c
+++ b/controller/statctrl.c
@@ -80,7 +80,8 @@ struct stats_node {
     };
 
 struct statctrl_ctx {
-    char *br_int;
+    /* OpenFlow connection to the switch. */
+    struct rconn *swconn;
 
     pthread_t thread;
     struct latch exit_latch;
@@ -95,8 +96,6 @@ static struct statctrl_ctx statctrl_ctx;
 static struct ovs_mutex mutex;
 
 static void *statctrl_thread_handler(void *arg);
-static void statctrl_rconn_setup(struct rconn *swconn, char *conn_target)
-    OVS_REQUIRES(mutex);
 static void statctrl_handle_rconn_msg(struct rconn *swconn,
                                       struct statctrl_ctx *ctx,
                                       struct ofpbuf *msg);
@@ -109,8 +108,6 @@ static void statctrl_send_request(struct rconn *swconn,
                                   struct statctrl_ctx *ctx)
     OVS_REQUIRES(mutex);
 static void statctrl_notify_main_thread(struct statctrl_ctx *ctx);
-static void statctrl_set_conn_target(const char *br_int_name)
-    OVS_REQUIRES(mutex);
 static void statctrl_wait_next_request(struct statctrl_ctx *ctx)
     OVS_REQUIRES(mutex);
 static bool statctrl_update_next_request_timestamp(struct stats_node *node,
@@ -121,7 +118,7 @@ static bool statctrl_update_next_request_timestamp(struct stats_node *node,
 void
 statctrl_init(void)
 {
-    statctrl_ctx.br_int = NULL;
+    statctrl_ctx.swconn = rconn_create(0, 0, DSCP_DEFAULT, 1 << OFP15_VERSION);
     latch_init(&statctrl_ctx.exit_latch);
     ovs_mutex_init(&mutex);
     statctrl_ctx.thread_seq = seq_create();
@@ -185,11 +182,14 @@ statctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
 }
 
 void
-statctrl_update(const char *br_int_name)
+statctrl_update_swconn(const char *target, int probe_interval)
 {
-    ovs_mutex_lock(&mutex);
-    statctrl_set_conn_target(br_int_name);
-    ovs_mutex_unlock(&mutex);
+    if (ovn_update_swconn_at(statctrl_ctx.swconn, target,
+                             probe_interval, "statctrl")) {
+        /* Notify statctrl thread that integration bridge
+         * target is set/changed. */
+        seq_change(statctrl_ctx.thread_seq);
+    }
 }
 
 void
@@ -217,7 +217,7 @@ statctrl_destroy(void)
     latch_set(&statctrl_ctx.exit_latch);
     pthread_join(statctrl_ctx.thread, NULL);
     latch_destroy(&statctrl_ctx.exit_latch);
-    free(statctrl_ctx.br_int);
+    rconn_destroy(statctrl_ctx.swconn);
     seq_destroy(statctrl_ctx.thread_seq);
     seq_destroy(statctrl_ctx.main_seq);
 
@@ -233,14 +233,9 @@ statctrl_thread_handler(void *arg)
     struct statctrl_ctx *ctx = arg;
 
     /* OpenFlow connection to the switch. */
-    struct rconn *swconn = rconn_create(0, 0, DSCP_DEFAULT,
-                                        1 << OFP15_VERSION);
+    struct rconn *swconn = ctx->swconn;
 
     while (!latch_is_set(&ctx->exit_latch)) {
-        ovs_mutex_lock(&mutex);
-        statctrl_rconn_setup(swconn, ctx->br_int);
-        ovs_mutex_unlock(&mutex);
-
         rconn_run(swconn);
         uint64_t new_seq = seq_read(ctx->thread_seq);
 
@@ -273,29 +268,9 @@ statctrl_thread_handler(void *arg)
         poll_block();
     }
 
-    rconn_destroy(swconn);
     return NULL;
 }
 
-static void
-statctrl_rconn_setup(struct rconn *swconn, char *br_int)
-    OVS_REQUIRES(mutex)
-{
-    if (!br_int) {
-        rconn_disconnect(swconn);
-        return;
-    }
-
-    char *conn_target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int);
-
-    if (strcmp(conn_target, rconn_get_target(swconn))) {
-        VLOG_INFO("%s: connecting to switch", conn_target);
-        rconn_connect(swconn, conn_target, conn_target);
-    }
-
-    free(conn_target);
-}
-
 static void
 statctrl_handle_rconn_msg(struct rconn *swconn, struct statctrl_ctx *ctx,
                           struct ofpbuf *msg)
@@ -400,23 +375,6 @@ statctrl_notify_main_thread(struct statctrl_ctx *ctx)
     }
 }
 
-static void
-statctrl_set_conn_target(const char *br_int_name)
-    OVS_REQUIRES(mutex)
-{
-    if (!br_int_name) {
-        return;
-    }
-
-
-    if (!statctrl_ctx.br_int || strcmp(statctrl_ctx.br_int, br_int_name)) {
-        free(statctrl_ctx.br_int);
-        statctrl_ctx.br_int = xstrdup(br_int_name);
-        /* Notify statctrl thread that integration bridge is set/changed. */
-        seq_change(statctrl_ctx.thread_seq);
-    }
-}
-
 static void
 statctrl_wait_next_request(struct statctrl_ctx *ctx)
     OVS_REQUIRES(mutex)
diff --git a/controller/statctrl.h b/controller/statctrl.h
index c5cede353..026ff387f 100644
--- a/controller/statctrl.h
+++ b/controller/statctrl.h
@@ -21,7 +21,8 @@
 void statctrl_init(void);
 void statctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
                   struct mac_cache_data *mac_cache_data);
-void statctrl_update(const char *br_int_name);
+
+void statctrl_update_swconn(const char *target, int probe_interval);
 void statctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn);
 void statctrl_destroy(void);
 
diff --git a/include/ovn/features.h b/include/ovn/features.h
index 08f1d8288..9e7c88b2a 100644
--- a/include/ovn/features.h
+++ b/include/ovn/features.h
@@ -49,7 +49,7 @@ enum ovs_feature_value {
 void ovs_feature_support_destroy(void);
 bool ovs_feature_is_supported(enum ovs_feature_value feature);
 bool ovs_feature_support_run(const struct smap *ovs_capabilities,
-                             const char *br_name);
+                             const char *conn_target, int probe_interval);
 bool ovs_feature_set_discovered(void);
 uint32_t ovs_feature_max_meters_get(void);
 uint32_t ovs_feature_max_select_groups_get(void);
diff --git a/lib/features.c b/lib/features.c
index b04437235..607e4bd31 100644
--- a/lib/features.c
+++ b/lib/features.c
@@ -29,8 +29,10 @@
 #include "openvswitch/ofp-meter.h"
 #include "openvswitch/ofp-group.h"
 #include "openvswitch/ofp-util.h"
+#include "openvswitch/rconn.h"
 #include "ovn/features.h"
 #include "controller/ofctrl.h"
+#include "ovn-util.h"
 
 VLOG_DEFINE_THIS_MODULE(features);
 
@@ -120,34 +122,12 @@ ovs_feature_is_supported(enum ovs_feature_value feature)
     return supported_ovs_features & feature;
 }
 
-static void
-ovs_feature_rconn_setup(const char *br_name)
-{
-    if (!swconn) {
-        swconn = rconn_create(0, 0, DSCP_DEFAULT, 1 << OFP15_VERSION);
-    }
-
-    if (!rconn_is_connected(swconn)) {
-        char *target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_name);
-        if (strcmp(target, rconn_get_target(swconn))) {
-            VLOG_INFO("%s: connecting to switch", target);
-            rconn_connect(swconn, target, target);
-        }
-        free(target);
-    }
-}
 
 static bool
-ovs_feature_get_openflow_cap(const char *br_name)
+ovs_feature_get_openflow_cap(void)
 {
     struct ofpbuf *msg;
 
-    if (!br_name) {
-        return false;
-    }
-
-    ovs_feature_rconn_setup(br_name);
-
     rconn_run(swconn);
     if (!rconn_is_connected(swconn)) {
         rconn_run_wait(swconn);
@@ -231,7 +211,7 @@ ovs_feature_support_destroy(void)
 /* Returns 'true' if the set of tracked OVS features has been updated. */
 bool
 ovs_feature_support_run(const struct smap *ovs_capabilities,
-                        const char *br_name)
+                        const char *conn_target, int probe_interval)
 {
     static struct smap empty_caps = SMAP_INITIALIZER(&empty_caps);
     bool updated = false;
@@ -240,7 +220,12 @@ ovs_feature_support_run(const struct smap *ovs_capabilities,
         ovs_capabilities = &empty_caps;
     }
 
-    if (ovs_feature_get_openflow_cap(br_name)) {
+    if (!swconn) {
+        swconn = rconn_create(0, 0, DSCP_DEFAULT, 1 << OFP15_VERSION);
+    }
+    ovn_update_swconn_at(swconn, conn_target, probe_interval, "features");
+
+    if (ovs_feature_get_openflow_cap()) {
         updated = true;
     }
 
diff --git a/lib/ovn-util.c b/lib/ovn-util.c
index ee5cbcdc3..4f50606c0 100644
--- a/lib/ovn-util.c
+++ b/lib/ovn-util.c
@@ -22,6 +22,7 @@
 #include "daemon.h"
 #include "include/ovn/actions.h"
 #include "openvswitch/ofp-parse.h"
+#include "openvswitch/rconn.h"
 #include "openvswitch/vlog.h"
 #include "lib/vswitch-idl.h"
 #include "ovn-dirs.h"
@@ -1284,3 +1285,28 @@ ovn_exit_args_finish(struct ovn_exit_args *exit_args)
     }
     free(exit_args->conns);
 }
+
+bool
+ovn_update_swconn_at(struct rconn *swconn, const char *target,
+                     int probe_interval, const char *where)
+{
+    if (!target) {
+        rconn_disconnect(swconn);
+        return true;
+    }
+
+    bool notify = false;
+
+    if (strcmp(target, rconn_get_target(swconn))) {
+        VLOG_INFO("%s: connecting to switch: \"%s\"", where, target);
+        rconn_connect(swconn, target, target);
+        notify = true;
+    }
+
+    if (probe_interval != rconn_get_probe_interval(swconn)) {
+        rconn_set_probe_interval(swconn, probe_interval);
+        notify = true;
+    }
+
+    return notify;
+}
diff --git a/lib/ovn-util.h b/lib/ovn-util.h
index 042e6bf82..f75b821b6 100644
--- a/lib/ovn-util.h
+++ b/lib/ovn-util.h
@@ -48,6 +48,7 @@ struct smap;
 struct svec;
 struct uuid;
 struct unixctl_conn;
+struct rconn;
 
 struct ipv4_netaddr {
     ovs_be32 addr;            /* 192.168.10.123 */
@@ -477,4 +478,7 @@ void ovn_exit_command_callback(struct unixctl_conn *conn, int argc,
                                const char *argv[], void *exit_args_);
 void ovn_exit_args_finish(struct ovn_exit_args *exit_args);
 
+bool ovn_update_swconn_at(struct rconn *swconn, const char *target,
+                          int probe_interval, const char *where);
+
 #endif /* OVN_UTIL_H */
diff --git a/lib/test-ovn-features.c b/lib/test-ovn-features.c
index aabc547e6..cddeae779 100644
--- a/lib/test-ovn-features.c
+++ b/lib/test-ovn-features.c
@@ -26,15 +26,15 @@ test_ovn_features(struct ovs_cmdl_context *ctx OVS_UNUSED)
     struct smap features = SMAP_INITIALIZER(&features);
 
     smap_add(&features, "ct_zero_snat", "false");
-    ovs_assert(!ovs_feature_support_run(&features, NULL));
+    ovs_assert(!ovs_feature_support_run(&features, NULL, 0));
     ovs_assert(!ovs_feature_is_supported(OVS_CT_ZERO_SNAT_SUPPORT));
 
     smap_replace(&features, "ct_zero_snat", "true");
-    ovs_assert(ovs_feature_support_run(&features, NULL));
+    ovs_assert(ovs_feature_support_run(&features, NULL, 0));
     ovs_assert(ovs_feature_is_supported(OVS_CT_ZERO_SNAT_SUPPORT));
 
     smap_add(&features, "unknown_feature", "true");
-    ovs_assert(!ovs_feature_support_run(&features, NULL));
+    ovs_assert(!ovs_feature_support_run(&features, NULL, 0));
 
     smap_destroy(&features);
 }
diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
index fdcc5aab2..e15cd02b0 100644
--- a/tests/ovn-controller.at
+++ b/tests/ovn-controller.at
@@ -2874,3 +2874,34 @@ AT_CHECK([test x"$port_uuid"=$(ovs-vsctl get port $fakech_tunnel _uuid)])
 OVN_CLEANUP([hv1])
 AT_CLEANUP
 ])
+
+AT_SETUP([ovn-controller - br-int remote])
+AT_KEYWORDS([ovn])
+ovn_start
+
+net_add n1
+sim_add hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.20
+ovs-vsctl -- add-port br-int vif1 -- \
+    set interface vif1 external-ids:iface-id=vif1
+
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+
+# Set the br-int remote and wait for the connection
+ovs-vsctl set-controller br-int ptcp:1234
+start_daemon ovn-controller --br-int-remote="tcp:127.0.0.1:1234" --br-int-probe-interval=5
+
+OVS_WAIT_UNTIL([test $(grep -c 'connecting to switch: "tcp:127.0.0.1:1234"' hv1/ovn-controller.log) = 4])
+OVS_WAIT_UNTIL([test $(grep -c 'tcp:127.0.0.1:1234: connected' hv1/ovn-controller.log) = 4])
+
+ovn-nbctl ls-add sw0
+
+check ovn-nbctl lsp-add sw0 vif1
+check ovn-nbctl lsp-set-addresses vif1 "00:00:00:00:00:01 192.168.0.10 1000::10"
+
+wait_for_ports_up
+ovn-nbctl --wait=hv sync
+
+OVN_CLEANUP([hv1])
+AT_CLEANUP
diff --git a/utilities/ovn-ctl b/utilities/ovn-ctl
index 700efe35a..ce83889d8 100755
--- a/utilities/ovn-ctl
+++ b/utilities/ovn-ctl
@@ -617,6 +617,12 @@ start_controller () {
     if test X"$OVN_CONTROLLER_SSL_CIPHERS" != X; then
         set "$@" --ssl-ciphers=$OVN_CONTROLLER_SSL_CIPHERS
     fi
+    if test X"$OVN_CONTROLLER_BR_INT_REMOTE" != X; then
+        set "$@" --br-int-remote=$OVN_CONTROLLER_BR_INT_REMOTE
+    fi
+    if test "$OVN_CONTROLLER_BR_INT_PROBE_INTERVAL" != 0; then
+        set "$@" --br-int-probe-interval=$OVN_CONTROLLER_BR_INT_PROBE_INTERVAL
+    fi
 
     [ "$OVN_USER" != "" ] && set "$@" --user "$OVN_USER"
 
@@ -831,6 +837,8 @@ set_defaults () {
     OVN_USER=
 
     OVN_CONTROLLER_LOG="-vconsole:emer -vsyslog:err -vfile:info"
+    OVN_CONTROLLER_BR_INT_REMOTE=""
+    OVN_CONTROLLER_BR_INT_PROBE_INTERVAL=0
     OVN_NORTHD_LOG="-vconsole:emer -vsyslog:err -vfile:info"
     OVN_NORTHD_LOGFILE=""
     OVN_NORTHD_N_THREADS=1
@@ -1041,6 +1049,8 @@ Options:
   --ovn-controller-ssl-bootstrap-ca-cert=CERT Bootstrapped OVN Southbound SSL CA certificate file
   --ovn-controller-ssl-protocols=PROTOCOLS OVN Southbound SSL protocols
   --ovn-controller-ssl-ciphers=CIPHERS OVN Southbound SSL cipher list
+  --ovn-controller-br-int-remote=REMOTE Active or passive connection to br-int
+  --ovn-controller-br-int-probe-interval=INTERVAL Probe interval in seconds for the br-int connection
   --ovn-nb-db-ssl-key=KEY OVN Northbound DB SSL private key file
   --ovn-nb-db-ssl-cert=CERT OVN Northbound DB SSL certificate file
   --ovn-nb-db-ssl-ca-cert=CERT OVN Northbound DB SSL CA certificate file
