meta-flow: Correctly set destination MAC in mf_set_flow_value().
[sliver-openvswitch.git] / lib / vconn.c
index 99b6e7b..6ea9366 100644 (file)
 #include "util.h"
 #include "vlog.h"
 
-VLOG_DEFINE_THIS_MODULE(vconn)
+VLOG_DEFINE_THIS_MODULE(vconn);
+
+COVERAGE_DEFINE(vconn_open);
+COVERAGE_DEFINE(vconn_received);
+COVERAGE_DEFINE(vconn_sent);
 
 /* State of an active vconn.*/
 enum vconn_state {
@@ -127,7 +131,7 @@ vconn_usage(bool active, bool passive, bool bootstrap OVS_UNUSED)
     /* Really this should be implemented via callbacks into the vconn
      * providers, but that seems too heavy-weight to bother with at the
      * moment. */
-    
+
     printf("\n");
     if (active) {
         printf("Active OpenFlow connection methods:\n");
@@ -279,7 +283,7 @@ vconn_open_block(const char *name, int min_version, struct vconn **vconnp)
 
     error = vconn_open(name, min_version, &vconn);
     if (!error) {
-        while ((error == vconn_connect(vconn)) == EAGAIN) {
+        while ((error = vconn_connect(vconn)) == EAGAIN) {
             vconn_run(vconn);
             vconn_run_wait(vconn);
             vconn_connect_wait(vconn);
@@ -317,39 +321,39 @@ vconn_get_name(const struct vconn *vconn)
 
 /* 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 yet known. */
-uint32_t
-vconn_get_remote_ip(const struct vconn *vconn) 
+ovs_be32
+vconn_get_remote_ip(const struct vconn *vconn)
 {
     return vconn->remote_ip;
 }
 
-/* Returns the transport port of the peer, or 0 if the connection does not 
+/* Returns the transport port of the peer, or 0 if the connection does not
  * contain a port or if the port is not yet known. */
-uint16_t
-vconn_get_remote_port(const struct vconn *vconn) 
+ovs_be16
+vconn_get_remote_port(const struct vconn *vconn)
 {
     return vconn->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 
+/* 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
  * yet known. */
-uint32_t
-vconn_get_local_ip(const struct vconn *vconn) 
+ovs_be32
+vconn_get_local_ip(const struct vconn *vconn)
 {
     return vconn->local_ip;
 }
 
-/* Returns the transport port used to connect to the peer, or 0 if the 
+/* 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 yet known. */
-uint16_t
-vconn_get_local_port(const struct vconn *vconn) 
+ovs_be16
+vconn_get_local_port(const struct vconn *vconn)
 {
     return vconn->local_port;
 }
 
 static void
-vcs_connecting(struct vconn *vconn) 
+vcs_connecting(struct vconn *vconn)
 {
     int retval = (vconn->class->connect)(vconn);
     assert(retval != EINPROGRESS);
@@ -461,10 +465,10 @@ vcs_send_error(struct vconn *vconn)
     }
 }
 
-/* Tries to complete the connection on 'vconn', which must be an active
- * vconn.  If 'vconn''s connection is complete, returns 0 if the connection
- * was successful or a positive errno value if it failed.  If the
- * connection is still in progress, returns EAGAIN. */
+/* Tries to complete the connection on 'vconn'. If 'vconn''s connection is
+ * complete, returns 0 if the connection was successful or a positive errno
+ * value if it failed.  If the connection is still in progress, returns
+ * EAGAIN. */
 int
 vconn_connect(struct vconn *vconn)
 {
@@ -504,11 +508,11 @@ vconn_connect(struct vconn *vconn)
     return EAGAIN;
 }
 
-/* Tries to receive an OpenFlow message from 'vconn', which must be an active
- * vconn.  If successful, stores the received message into '*msgp' and returns
- * 0.  The caller is responsible for destroying the message with
- * ofpbuf_delete().  On failure, returns a positive errno value and stores a
- * null pointer into '*msgp'.  On normal connection close, returns EOF.
+/* Tries to receive an OpenFlow message from 'vconn'.  If successful, stores
+ * the received message into '*msgp' and returns 0.  The caller is responsible
+ * for destroying the message with ofpbuf_delete().  On failure, returns a
+ * positive errno value and stores a null pointer into '*msgp'.  On normal
+ * connection close, returns EOF.
  *
  * vconn_recv will not block waiting for a packet to arrive.  If no packets
  * have been received, it returns EAGAIN immediately. */
@@ -565,11 +569,10 @@ do_recv(struct vconn *vconn, struct ofpbuf **msgp)
     return retval;
 }
 
-/* Tries to queue 'msg' for transmission on 'vconn', which must be an active
- * vconn.  If successful, returns 0, in which case ownership of 'msg' is
- * transferred to the vconn.  Success does not guarantee that 'msg' has been or
- * ever will be delivered to the peer, only that it has been queued for
- * transmission.
+/* Tries to queue 'msg' for transmission on 'vconn'.  If successful, returns 0,
+ * in which case ownership of 'msg' is transferred to the vconn.  Success does
+ * not guarantee that 'msg' has been or ever will be delivered to the peer,
+ * only that it has been queued for transmission.
  *
  * Returns a positive errno value on failure, in which case the caller
  * retains ownership of 'msg'.
@@ -649,10 +652,10 @@ vconn_recv_block(struct vconn *vconn, struct ofpbuf **msgp)
  *
  * 'request' is always destroyed, regardless of the return value. */
 int
-vconn_recv_xid(struct vconn *vconn, uint32_t xid, struct ofpbuf **replyp)
+vconn_recv_xid(struct vconn *vconn, ovs_be32 xid, struct ofpbuf **replyp)
 {
     for (;;) {
-        uint32_t recv_xid;
+        ovs_be32 recv_xid;
         struct ofpbuf *reply;
         int error;
 
@@ -668,7 +671,8 @@ vconn_recv_xid(struct vconn *vconn, uint32_t xid, struct ofpbuf **replyp)
         }
 
         VLOG_DBG_RL(&bad_ofmsg_rl, "%s: received reply with xid %08"PRIx32
-                    " != expected %08"PRIx32, vconn->name, recv_xid, xid);
+                    " != expected %08"PRIx32,
+                    vconn->name, ntohl(recv_xid), ntohl(xid));
         ofpbuf_delete(reply);
     }
 }
@@ -678,12 +682,16 @@ vconn_recv_xid(struct vconn *vconn, uint32_t xid, struct ofpbuf **replyp)
  * 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' should be an OpenFlow request that requires a reply.  Otherwise,
+ * if there is no reply, this function can end up blocking forever (or until
+ * the peer drops the connection).
+ *
  * '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;
+    ovs_be32 send_xid = ((struct ofp_header *) request->data)->xid;
     int error;
 
     *replyp = NULL;
@@ -694,6 +702,104 @@ vconn_transact(struct vconn *vconn, struct ofpbuf *request,
     return error ? error : vconn_recv_xid(vconn, send_xid, replyp);
 }
 
+/* Sends 'request' followed by a barrier request to 'vconn', then blocks until
+ * it receives a reply to the barrier.  If successful, stores the reply to
+ * 'request' in '*replyp', if one was received, and otherwise NULL, then
+ * returns 0.  Otherwise returns a positive errno value, or EOF, and sets
+ * '*replyp' to null.
+ *
+ * This function is useful for sending an OpenFlow request that doesn't
+ * ordinarily include a reply but might report an error in special
+ * circumstances.
+ *
+ * 'request' is always destroyed, regardless of the return value. */
+int
+vconn_transact_noreply(struct vconn *vconn, struct ofpbuf *request,
+                       struct ofpbuf **replyp)
+{
+    ovs_be32 request_xid;
+    ovs_be32 barrier_xid;
+    struct ofpbuf *barrier;
+    int error;
+
+    *replyp = NULL;
+
+    /* Send request. */
+    request_xid = ((struct ofp_header *) request->data)->xid;
+    error = vconn_send_block(vconn, request);
+    if (error) {
+        ofpbuf_delete(request);
+        return error;
+    }
+
+    /* Send barrier. */
+    make_openflow(sizeof(struct ofp_header), OFPT_BARRIER_REQUEST, &barrier);
+    barrier_xid = ((struct ofp_header *) barrier->data)->xid;
+    error = vconn_send_block(vconn, barrier);
+    if (error) {
+        ofpbuf_delete(barrier);
+        return error;
+    }
+
+    for (;;) {
+        struct ofpbuf *msg;
+        ovs_be32 msg_xid;
+        int error;
+
+        error = vconn_recv_block(vconn, &msg);
+        if (error) {
+            ofpbuf_delete(*replyp);
+            *replyp = NULL;
+            return error;
+        }
+
+        msg_xid = ((struct ofp_header *) msg->data)->xid;
+        if (msg_xid == request_xid) {
+            if (*replyp) {
+                VLOG_WARN_RL(&bad_ofmsg_rl, "%s: duplicate replies with "
+                             "xid %08"PRIx32, vconn->name, ntohl(msg_xid));
+                ofpbuf_delete(*replyp);
+            }
+            *replyp = msg;
+        } else {
+            ofpbuf_delete(msg);
+            if (msg_xid == barrier_xid) {
+                return 0;
+            } else {
+                VLOG_DBG_RL(&bad_ofmsg_rl, "%s: reply with xid %08"PRIx32
+                            " != expected %08"PRIx32" or %08"PRIx32,
+                            vconn->name, ntohl(msg_xid),
+                            ntohl(request_xid), ntohl(barrier_xid));
+            }
+        }
+    }
+}
+
+/* vconn_transact_noreply() for a list of "struct ofpbuf"s, sent one by one.
+ * All of the requests on 'requests' are always destroyed, regardless of the
+ * return value. */
+int
+vconn_transact_multiple_noreply(struct vconn *vconn, struct list *requests,
+                                struct ofpbuf **replyp)
+{
+    struct ofpbuf *request, *next;
+
+    LIST_FOR_EACH_SAFE (request, next, list_node, requests) {
+        int error;
+
+        list_remove(&request->list_node);
+
+        error = vconn_transact_noreply(vconn, request, replyp);
+        if (error || *replyp) {
+            ofpbuf_list_delete(requests);
+            return error;
+        }
+    }
+
+    *replyp = NULL;
+    return 0;
+}
+
 void
 vconn_wait(struct vconn *vconn, enum vconn_wait_type wait)
 {
@@ -903,25 +1009,25 @@ vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
 }
 
 void
-vconn_set_remote_ip(struct vconn *vconn, uint32_t ip)
+vconn_set_remote_ip(struct vconn *vconn, ovs_be32 ip)
 {
     vconn->remote_ip = ip;
 }
 
 void
-vconn_set_remote_port(struct vconn *vconn, uint16_t port)
+vconn_set_remote_port(struct vconn *vconn, ovs_be16 port)
 {
     vconn->remote_port = port;
 }
 
-void 
-vconn_set_local_ip(struct vconn *vconn, uint32_t ip)
+void
+vconn_set_local_ip(struct vconn *vconn, ovs_be32 ip)
 {
     vconn->local_ip = ip;
 }
 
-void 
-vconn_set_local_port(struct vconn *vconn, uint16_t port)
+void
+vconn_set_local_port(struct vconn *vconn, ovs_be16 port)
 {
     vconn->local_port = port;
 }