rconn: Fix segfault when the idle timeout races with connection failure.
[sliver-openvswitch.git] / lib / rconn.c
index 3865fc9..c8bddab 100644 (file)
@@ -39,7 +39,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include "ofpbuf.h"
-#include "openflow.h"
+#include "openflow/openflow.h"
 #include "poll-loop.h"
 #include "sat-math.h"
 #include "timeval.h"
@@ -264,10 +264,15 @@ void
 rconn_destroy(struct rconn *rc)
 {
     if (rc) {
+        size_t i;
+
         free(rc->name);
         vconn_close(rc->vconn);
         flush_queue(rc);
         queue_destroy(&rc->txq);
+        for (i = 0; i < rc->n_monitors; i++) {
+            vconn_close(rc->monitors[i]);
+        }
         free(rc);
     }
 }
@@ -375,10 +380,14 @@ run_ACTIVE(struct rconn *rc)
 {
     if (timed_out(rc)) {
         unsigned int base = MAX(rc->last_received, rc->state_entered);
-        rconn_send(rc, make_echo_request(), NULL);
         VLOG_DBG("%s: idle %u seconds, sending inactivity probe",
                  rc->name, (unsigned int) (time_now() - base));
+
+        /* Ordering is important here: rconn_send() can transition to BACKOFF,
+         * and we don't want to transition back to IDLE if so, because then we
+         * can end up queuing a packet with vconn == NULL and then *boom*. */
         state_transition(rc, S_IDLE);
+        rconn_send(rc, make_echo_request(), NULL);
         return;
     }
 
@@ -582,18 +591,15 @@ rconn_is_connected(const struct rconn *rconn)
     return is_connected_state(rconn->state);
 }
 
-/* Returns 0 if 'rconn' is connected and the connection is believed to have
- * been accepted by the controller.  Otherwise, if 'rconn' is in a "failure
- * mode" (that is, it is not connected or if it has recently connected and the
- * controller is not yet believed to have made an admission control decision
- * for this switch), returns the number of seconds that it has been in failure
- * mode. */
+/* Returns 0 if 'rconn' is connected.  Otherwise, if 'rconn' is in a "failure
+ * mode" (that is, it is not connected), returns the number of seconds that it
+ * has been in failure mode, ignoring any times that it connected but the
+ * controller's admission control policy caused it to be quickly
+ * disconnected. */
 int
 rconn_failure_duration(const struct rconn *rconn)
 {
-    return (rconn_is_connected(rconn) && rconn->probably_admitted
-            ? 0
-            : time_now() - rconn->last_admitted);
+    return rconn_is_connected(rconn) ? 0 : time_now() - rconn->last_admitted;
 }
 
 /* Returns the IP address of the peer, or 0 if the peer is not connected over