+static void fail_open_recover(struct fail_open *);
+
+/* Returns the number of seconds of disconnection after which fail-open mode
+ * should activate. */
+static int
+trigger_duration(const struct fail_open *fo)
+{
+ if (!connmgr_has_controllers(fo->connmgr)) {
+ /* Shouldn't ever arrive here, but if we do, never fail open. */
+ return INT_MAX;
+ } else {
+ /* Otherwise, every controller must have a chance to send an
+ * inactivity probe and reconnect before we fail open, so take the
+ * maximum probe interval and multiply by 3:
+ *
+ * - The first interval is the idle time before sending an inactivity
+ * probe.
+ *
+ * - The second interval is the time allowed for a response to the
+ * inactivity probe.
+ *
+ * - The third interval is the time allowed to reconnect after no
+ * response is received.
+ */
+ return connmgr_get_max_probe_interval(fo->connmgr) * 3;
+ }
+}
+
+/* Returns true if 'fo' is currently in fail-open mode, otherwise false. */
+bool
+fail_open_is_active(const struct fail_open *fo)
+{
+ return fo->last_disconn_secs != 0;
+}
+
+static void
+send_bogus_packet_ins(struct fail_open *fo)
+{
+ struct eth_addr mac;
+ struct dp_packet b;
+
+ dp_packet_init(&b, 128);
+ eth_addr_nicira_random(&mac);
+ compose_rarp(&b, mac);
+
+ struct ofproto_async_msg am = {
+ .oam = OAM_PACKET_IN,
+ .pin = {
+ .up = {
+ .public = {
+ .packet = dp_packet_data(&b),
+ .packet_len = dp_packet_size(&b),
+ .flow_metadata = MATCH_CATCHALL_INITIALIZER,
+ .flow_metadata.flow.in_port.ofp_port = OFPP_LOCAL,
+ .flow_metadata.wc.masks.in_port.ofp_port
+ = u16_to_ofp(UINT16_MAX),
+ .reason = OFPR_NO_MATCH,
+ .cookie = OVS_BE64_MAX,
+ },
+ },
+ .max_len = UINT16_MAX,
+ }
+ };
+ connmgr_send_async_msg(fo->connmgr, &am);
+
+ dp_packet_uninit(&b);
+}
+
+/* Enter fail-open mode if we should be in it. */