+ vconn_send_wait(vconn);
+ poll_block();
+ }
+ return retval;
+}
+
+/* Same as vconn_recv, except that it waits until a message is received. */
+int
+vconn_recv_block(struct vconn *vconn, struct ofpbuf **msgp)
+{
+ int retval;
+ while ((retval = vconn_recv(vconn, msgp)) == EAGAIN) {
+ vconn_recv_wait(vconn);
+ poll_block();
+ }
+ return retval;
+}
+
+/* Waits until a message with a transaction ID matching 'xid' is recived on
+ * 'vconn'. Returns 0 if successful, in which case the reply is stored in
+ * '*replyp' for the caller to examine and free. Otherwise returns a positive
+ * errno value, or EOF, and sets '*replyp' to null.
+ *
+ * 'request' is always destroyed, regardless of the return value. */
+int
+vconn_recv_xid(struct vconn *vconn, uint32_t xid, struct ofpbuf **replyp)
+{
+ for (;;) {
+ uint32_t recv_xid;
+ struct ofpbuf *reply;
+ int error;
+
+ error = vconn_recv_block(vconn, &reply);
+ if (error) {
+ *replyp = NULL;
+ return error;
+ }
+ recv_xid = ((struct ofp_header *) reply->data)->xid;
+ if (xid == recv_xid) {
+ *replyp = reply;
+ return 0;
+ }
+
+ VLOG_DBG_RL(&rl, "%s: received reply with xid %08"PRIx32" != expected "
+ "%08"PRIx32, vconn->name, recv_xid, xid);
+ ofpbuf_delete(reply);
+ }
+}
+
+/* Sends 'request' to 'vconn' and blocks until it receives a reply with a
+ * matching transaction ID. Returns 0 if successful, in which case the reply
+ * is stored in '*replyp' for the caller to examine and free. Otherwise
+ * returns a positive errno value, or EOF, and sets '*replyp' to null.
+ *
+ * 'request' is always destroyed, regardless of the return value. */
+int
+vconn_transact(struct vconn *vconn, struct ofpbuf *request,
+ struct ofpbuf **replyp)
+{
+ uint32_t send_xid = ((struct ofp_header *) request->data)->xid;
+ int error;
+
+ *replyp = NULL;
+ error = vconn_send_block(vconn, request);
+ if (error) {
+ ofpbuf_delete(request);
+ }
+ return error ? error : vconn_recv_xid(vconn, send_xid, replyp);
+}
+
+void
+vconn_wait(struct vconn *vconn, enum vconn_wait_type wait)
+{
+ assert(wait == WAIT_CONNECT || wait == WAIT_RECV || wait == WAIT_SEND);
+
+ switch (vconn->state) {
+ case VCS_CONNECTING:
+ wait = WAIT_CONNECT;
+ break;
+
+ case VCS_SEND_HELLO:
+ case VCS_SEND_ERROR:
+ wait = WAIT_SEND;
+ break;
+
+ case VCS_RECV_HELLO:
+ wait = WAIT_RECV;
+ break;
+
+ case VCS_CONNECTED:
+ break;
+
+ case VCS_DISCONNECTED:
+ poll_immediate_wake();
+ return;
+ }
+ (vconn->class->wait)(vconn, wait);
+}
+
+void
+vconn_connect_wait(struct vconn *vconn)
+{
+ vconn_wait(vconn, WAIT_CONNECT);
+}
+
+void
+vconn_recv_wait(struct vconn *vconn)
+{
+ vconn_wait(vconn, WAIT_RECV);
+}
+
+void
+vconn_send_wait(struct vconn *vconn)
+{
+ vconn_wait(vconn, WAIT_SEND);
+}
+
+/* Attempts to start listening for OpenFlow connections. 'name' is a
+ * connection name in the form "TYPE:ARGS", where TYPE is an passive vconn
+ * class's name and ARGS are vconn class-specific.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. If successful,
+ * stores a pointer to the new connection in '*pvconnp', otherwise a null
+ * pointer. */
+int
+pvconn_open(const char *name, struct pvconn **pvconnp)
+{
+ size_t prefix_len;
+ size_t i;
+
+ check_vconn_classes();
+
+ *pvconnp = NULL;
+ prefix_len = strcspn(name, ":");
+ if (prefix_len == strlen(name)) {
+ return EAFNOSUPPORT;
+ }
+ for (i = 0; i < ARRAY_SIZE(pvconn_classes); i++) {
+ struct pvconn_class *class = pvconn_classes[i];
+ if (strlen(class->name) == prefix_len
+ && !memcmp(class->name, name, prefix_len)) {
+ char *suffix_copy = xstrdup(name + prefix_len + 1);
+ int retval = class->listen(name, suffix_copy, pvconnp);
+ free(suffix_copy);
+ if (retval) {
+ *pvconnp = NULL;
+ }
+ return retval;