Merge citrix branch into master.
[sliver-openvswitch.git] / lib / rconn.c
index deb9519..2cbe43e 100644 (file)
@@ -89,7 +89,7 @@ struct rconn {
     time_t last_admitted;
 
     /* These values are simply for statistics reporting, not used directly by
-     * anything internal to the rconn (or the secchan for that matter). */
+     * anything internal to the rconn (or ofproto for that matter). */
     unsigned int packets_received;
     unsigned int n_attempted_connections, n_successful_connections;
     time_t creation_time;
@@ -111,6 +111,18 @@ struct rconn {
      * a response. */
     int probe_interval;         /* Secs of inactivity before sending probe. */
 
+    /* When we create a vconn we obtain these values, to save them past the end
+     * of the vconn's lifetime.  Otherwise, in-band control will only allow
+     * traffic when a vconn is actually open, but it is nice to allow ARP to
+     * complete even between connection attempts, and it is also polite to
+     * allow traffic from other switches to go through to the controller
+     * whether or not we are connected.
+     *
+     * We don't cache the local port, because that changes from one connection
+     * attempt to the next. */
+    uint32_t local_ip, remote_ip;
+    uint16_t remote_port;
+
     /* Messages sent or received are copied to the monitor connections. */
 #define MAX_MONITORS 8
     struct vconn *monitors[8];
@@ -121,6 +133,7 @@ static unsigned int elapsed_in_this_state(const struct rconn *);
 static unsigned int timeout(const struct rconn *);
 static bool timed_out(const struct rconn *);
 static void state_transition(struct rconn *, enum state);
+static void set_vconn_name(struct rconn *, const char *name);
 static int try_send(struct rconn *);
 static int reconnect(struct rconn *);
 static void disconnect(struct rconn *, int error);
@@ -159,7 +172,7 @@ rconn_new_from_vconn(const char *name, struct vconn *vconn)
  * 'max_backoff' is the maximum number of seconds between attempts to connect
  * to the peer.  The actual interval starts at 1 second and doubles on each
  * failure until it reaches 'max_backoff'.  If 0 is specified, the default of
- * 60 seconds is used. */
+ * 8 seconds is used. */
 struct rconn *
 rconn_create(int probe_interval, int max_backoff)
 {
@@ -175,7 +188,7 @@ rconn_create(int probe_interval, int max_backoff)
     queue_init(&rc->txq);
 
     rc->backoff = 0;
-    rc->max_backoff = max_backoff ? max_backoff : 60;
+    rc->max_backoff = max_backoff ? max_backoff : 8;
     rc->backoff_deadline = TIME_MIN;
     rc->last_received = time_now();
     rc->last_connected = time_now();
@@ -236,8 +249,7 @@ int
 rconn_connect(struct rconn *rc, const char *name)
 {
     rconn_disconnect(rc);
-    free(rc->name);
-    rc->name = xstrdup(name);
+    set_vconn_name(rc, name);
     rc->reliable = true;
     return reconnect(rc);
 }
@@ -248,8 +260,7 @@ rconn_connect_unreliably(struct rconn *rc,
 {
     assert(vconn != NULL);
     rconn_disconnect(rc);
-    free(rc->name);
-    rc->name = xstrdup(name);
+    set_vconn_name(rc, name);
     rc->reliable = false;
     rc->vconn = vconn;
     rc->last_connected = time_now();
@@ -273,8 +284,7 @@ rconn_disconnect(struct rconn *rc)
             vconn_close(rc->vconn);
             rc->vconn = NULL;
         }
-        free(rc->name);
-        rc->name = xstrdup("void");
+        set_vconn_name(rc, "void");
         rc->reliable = false;
 
         rc->backoff = 0;
@@ -323,6 +333,9 @@ reconnect(struct rconn *rc)
     rc->n_attempted_connections++;
     retval = vconn_open(rc->name, OFP_VERSION, &rc->vconn);
     if (!retval) {
+        rc->remote_ip = vconn_get_remote_ip(rc->vconn);
+        rc->local_ip = vconn_get_local_ip(rc->vconn);
+        rc->remote_port = vconn_get_remote_port(rc->vconn);
         rc->backoff_deadline = time_now() + rc->backoff;
         state_transition(rc, S_CONNECTING);
     } else {
@@ -486,7 +499,7 @@ rconn_recv(struct rconn *rc)
         int error = vconn_recv(rc->vconn, &buffer);
         if (!error) {
             copy_to_monitor(rc, buffer);
-            if (is_admitted_msg(buffer)
+            if (rc->probably_admitted || is_admitted_msg(buffer)
                 || time_now() - rc->last_connected >= 30) {
                 rc->probably_admitted = true;
                 rc->last_admitted = time_now();
@@ -624,23 +637,55 @@ rconn_is_connected(const struct rconn *rconn)
     return is_connected_state(rconn->state);
 }
 
-/* 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. */
+/* Returns true if 'rconn' is connected and thought to have been accepted by
+ * the peer's admission-control policy. */
+bool
+rconn_is_admitted(const struct rconn *rconn)
+{
+    return (rconn_is_connected(rconn)
+            && rconn->last_admitted >= rconn->last_connected);
+}
+
+/* Returns 0 if 'rconn' is currently connected and considered to have been
+ * accepted by the peer's admission-control policy, otherwise the number of
+ * seconds since 'rconn' was last in such a state. */
 int
 rconn_failure_duration(const struct rconn *rconn)
 {
-    return rconn_is_connected(rconn) ? 0 : time_now() - rconn->last_admitted;
+    return rconn_is_admitted(rconn) ? 0 : time_now() - rconn->last_admitted;
 }
 
-/* Returns the IP address of the peer, or 0 if the peer is not connected over
- * an IP-based protocol or if its IP address is not known. */
+/* Returns the IP address of the peer, or 0 if the peer's IP address is not
+ * known. */
 uint32_t
-rconn_get_ip(const struct rconn *rconn) 
+rconn_get_remote_ip(const struct rconn *rconn) 
+{
+    return rconn->remote_ip;
+}
+
+/* Returns the transport port of the peer, or 0 if the peer's port is not
+ * known. */
+uint16_t
+rconn_get_remote_port(const struct rconn *rconn) 
 {
-    return rconn->vconn ? vconn_get_ip(rconn->vconn) : 0;
+    return rconn->remote_port;
+}
+
+/* Returns the IP address used to connect to the peer, or 0 if the
+ * connection is not an IP-based protocol or if its IP address is not 
+ * known. */
+uint32_t
+rconn_get_local_ip(const struct rconn *rconn) 
+{
+    return rconn->local_ip;
+}
+
+/* Returns the transport port used to connect to the peer, or 0 if the
+ * connection does not contain a port or if the port is not known. */
+uint16_t
+rconn_get_local_port(const struct rconn *rconn) 
+{
+    return rconn->vconn ? vconn_get_local_port(rconn->vconn) : 0;
 }
 
 /* If 'rconn' can't connect to the peer, it could be for any number of reasons.
@@ -698,6 +743,15 @@ rconn_get_last_connection(const struct rconn *rc)
     return rc->last_connected;
 }
 
+/* Returns the time at which the last OpenFlow message was received by 'rc'.
+ * If no packets have been received on 'rc', returns the time at which 'rc'
+ * was created. */
+time_t
+rconn_get_last_received(const struct rconn *rc)
+{
+    return rc->last_received;
+}
+
 /* Returns the time at which 'rc' was created. */
 time_t
 rconn_get_creation_time(const struct rconn *rc)
@@ -771,6 +825,19 @@ rconn_packet_counter_dec(struct rconn_packet_counter *c)
     }
 }
 \f
+/* Set the name of the remote vconn to 'name' and clear out the cached IP
+ * address and port information, since changing the name also likely changes
+ * these values. */
+static void
+set_vconn_name(struct rconn *rc, const char *name)
+{
+    free(rc->name);
+    rc->name = xstrdup(name);
+    rc->local_ip = 0;
+    rc->remote_ip = 0;
+    rc->remote_port = 0;
+}
+
 /* Tries to send a packet from 'rc''s send buffer.  Returns 0 if successful,
  * otherwise a positive errno value. */
 static int