Merge branch "partner", to simplify partner integration.
[sliver-openvswitch.git] / lib / rconn.c
index 7eb93ba..fabd61e 100644 (file)
@@ -39,6 +39,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include "ofpbuf.h"
+#include "openflow.h"
 #include "poll-loop.h"
 #include "sat-math.h"
 #include "timeval.h"
@@ -113,6 +114,11 @@ struct rconn {
      * an echo request as an inactivity probe packet.  We should receive back
      * a response. */
     int probe_interval;         /* Secs of inactivity before sending probe. */
+
+    /* Messages sent or received are copied to the monitor connections. */
+#define MAX_MONITORS 8
+    struct vconn *monitors[8];
+    size_t n_monitors;
 };
 
 static unsigned int elapsed_in_this_state(const struct rconn *);
@@ -124,6 +130,7 @@ static int reconnect(struct rconn *);
 static void disconnect(struct rconn *, int error);
 static void flush_queue(struct rconn *);
 static void question_connectivity(struct rconn *);
+static void copy_to_monitor(struct rconn *, const struct ofpbuf *);
 
 /* Creates a new rconn, connects it (reliably) to 'name', and returns it. */
 struct rconn *
@@ -188,6 +195,8 @@ rconn_create(int probe_interval, int max_backoff)
 
     rc->probe_interval = probe_interval ? MAX(5, probe_interval) : 0;
 
+    rc->n_monitors = 0;
+
     return rc;
 }
 
@@ -264,7 +273,7 @@ reconnect(struct rconn *rc)
 
     VLOG_WARN("%s: connecting...", rc->name);
     rc->n_attempted_connections++;
-    retval = vconn_open(rc->name, &rc->vconn);
+    retval = vconn_open(rc->name, OFP_VERSION, &rc->vconn);
     if (!retval) {
         rc->backoff_deadline = time_now() + rc->backoff;
         state_transition(rc, S_CONNECTING);
@@ -302,13 +311,8 @@ run_CONNECTING(struct rconn *rc)
     if (!retval) {
         VLOG_WARN("%s: connected", rc->name);
         rc->n_successful_connections++;
-        if (vconn_is_passive(rc->vconn)) {
-            ofp_error(0, "%s: passive vconn not supported", rc->name);
-            state_transition(rc, S_VOID);
-        } else {
-            state_transition(rc, S_ACTIVE);
-            rc->last_connected = rc->state_entered;
-        }
+        state_transition(rc, S_ACTIVE);
+        rc->last_connected = rc->state_entered;
     } else if (retval != EAGAIN) {
         VLOG_WARN("%s: connection failed (%s)", rc->name, strerror(retval));
         disconnect(rc, retval);
@@ -428,6 +432,7 @@ rconn_recv(struct rconn *rc)
         struct ofpbuf *buffer;
         int error = vconn_recv(rc->vconn, &buffer);
         if (!error) {
+            copy_to_monitor(rc, buffer);
             rc->last_received = time_now();
             rc->packets_received++;
             if (rc->state == S_IDLE) {
@@ -468,6 +473,7 @@ int
 rconn_send(struct rconn *rc, struct ofpbuf *b, int *n_queued)
 {
     if (rconn_is_connected(rc)) {
+        copy_to_monitor(rc, b);
         b->private = n_queued;
         if (n_queued) {
             ++*n_queued;
@@ -485,7 +491,7 @@ rconn_send(struct rconn *rc, struct ofpbuf *b, int *n_queued)
 /* Sends 'b' on 'rc'.  Increments '*n_queued' while the packet is in flight; it
  * will be decremented when it has been sent (or discarded due to
  * disconnection).  Returns 0 if successful, EAGAIN if '*n_queued' is already
- * at least as large of 'queue_limit', or ENOTCONN if 'rc' is not currently
+ * at least as large as 'queue_limit', or ENOTCONN if 'rc' is not currently
  * connected.  Regardless of return value, 'b' is destroyed.
  *
  * Because 'b' may be sent (or discarded) before this function returns, the
@@ -515,6 +521,21 @@ rconn_packets_sent(const struct rconn *rc)
     return rc->packets_sent;
 }
 
+/* Adds 'vconn' to 'rc' as a monitoring connection, to which all messages sent
+ * and received on 'rconn' will be copied.  'rc' takes ownership of 'vconn'. */
+void
+rconn_add_monitor(struct rconn *rc, struct vconn *vconn)
+{
+    if (rc->n_monitors < ARRAY_SIZE(rc->monitors)) {
+        VLOG_WARN("new monitor connection from %s", vconn_get_name(vconn));
+        rc->monitors[rc->n_monitors++] = vconn;
+    } else {
+        VLOG_DBG("too many monitor connections, discarding %s",
+                 vconn_get_name(vconn));
+        vconn_close(vconn);
+    }
+}
+
 /* Returns 'rc''s name (the 'name' argument passed to rconn_new()). */
 const char *
 rconn_get_name(const struct rconn *rc)
@@ -766,3 +787,31 @@ question_connectivity(struct rconn *rc)
         rc->last_questioned = now;
     }
 }
+
+static void
+copy_to_monitor(struct rconn *rc, const struct ofpbuf *b)
+{
+    struct ofpbuf *clone = NULL;
+    int retval;
+    size_t i;
+
+    for (i = 0; i < rc->n_monitors; ) {
+        struct vconn *vconn = rc->monitors[i];
+
+        if (!clone) {
+            clone = ofpbuf_clone(b);
+        }
+        retval = vconn_send(vconn, clone);
+        if (!retval) {
+            clone = NULL;
+        } else if (retval != EAGAIN) {
+            VLOG_DBG("%s: closing monitor connection to %s: %s",
+                     rconn_get_name(rc), vconn_get_name(vconn),
+                     strerror(retval));
+            rc->monitors[i] = rc->monitors[--rc->n_monitors];
+            continue;
+        }
+        i++;
+    }
+    ofpbuf_delete(clone);
+}