Global replace of Nicira Networks.
[sliver-openvswitch.git] / lib / vconn.c
index ec0ac4e..5cd708d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
 #include "dynamic-string.h"
 #include "fatal-signal.h"
 #include "flow.h"
+#include "ofp-errors.h"
 #include "ofp-print.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "random.h"
 #include "util.h"
 #include "vlog.h"
+#include "socket-util.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 {
@@ -208,13 +214,14 @@ vconn_verify_name(const char *name)
  *
  * The vconn will automatically negotiate an OpenFlow protocol version
  * acceptable to both peers on the connection.  The version negotiated will be
- * no lower than 'min_version' and no higher than OFP_VERSION.
+ * no lower than 'min_version' and no higher than OFP10_VERSION.
  *
  * Returns 0 if successful, otherwise a positive errno value.  If successful,
  * stores a pointer to the new connection in '*vconnp', otherwise a null
  * pointer.  */
 int
-vconn_open(const char *name, int min_version, struct vconn **vconnp)
+vconn_open(const char *name, int min_version, struct vconn **vconnp,
+           uint8_t dscp)
 {
     struct vconn_class *class;
     struct vconn *vconn;
@@ -232,7 +239,7 @@ vconn_open(const char *name, int min_version, struct vconn **vconnp)
 
     /* Call class's "open" function. */
     suffix_copy = xstrdup(strchr(name, ':') + 1);
-    error = class->open(name, suffix_copy, &vconn);
+    error = class->open(name, suffix_copy, &vconn, dscp);
     free(suffix_copy);
     if (error) {
         goto error;
@@ -277,9 +284,9 @@ vconn_open_block(const char *name, int min_version, struct vconn **vconnp)
 
     fatal_signal_run();
 
-    error = vconn_open(name, min_version, &vconn);
+    error = vconn_open(name, min_version, &vconn, DSCP_DEFAULT);
     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,7 +324,7 @@ 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
+ovs_be32
 vconn_get_remote_ip(const struct vconn *vconn)
 {
     return vconn->remote_ip;
@@ -325,7 +332,7 @@ vconn_get_remote_ip(const struct vconn *vconn)
 
 /* 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
+ovs_be16
 vconn_get_remote_port(const struct vconn *vconn)
 {
     return vconn->remote_port;
@@ -334,7 +341,7 @@ vconn_get_remote_port(const struct vconn *vconn)
 /* 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
+ovs_be32
 vconn_get_local_ip(const struct vconn *vconn)
 {
     return vconn->local_ip;
@@ -342,12 +349,23 @@ vconn_get_local_ip(const struct vconn *vconn)
 
 /* 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
+ovs_be16
 vconn_get_local_port(const struct vconn *vconn)
 {
     return vconn->local_port;
 }
 
+/* Returns the OpenFlow version negotiated with the peer, or -1 if version
+ * negotiation is not yet complete.
+ *
+ * A vconn that has successfully connected (that is, vconn_connect() or
+ * vconn_send() or vconn_recv() has returned 0) always negotiated a version. */
+int
+vconn_get_version(const struct vconn *vconn)
+{
+    return vconn->version;
+}
+
 static void
 vcs_connecting(struct vconn *vconn)
 {
@@ -399,13 +417,13 @@ vcs_recv_hello(struct vconn *vconn)
                 ds_destroy(&msg);
             }
 
-            vconn->version = MIN(OFP_VERSION, oh->version);
+            vconn->version = MIN(OFP10_VERSION, oh->version);
             if (vconn->version < vconn->min_version) {
                 VLOG_WARN_RL(&bad_ofmsg_rl,
                              "%s: version negotiation failed: we support "
                              "versions 0x%02x to 0x%02x inclusive but peer "
                              "supports no later than version 0x%02"PRIx8,
-                             vconn->name, vconn->min_version, OFP_VERSION,
+                             vconn->name, vconn->min_version, OFP10_VERSION,
                              oh->version);
                 vconn->state = VCS_SEND_ERROR;
             } else {
@@ -413,7 +431,7 @@ vcs_recv_hello(struct vconn *vconn)
                          "(we support versions 0x%02x to 0x%02x inclusive, "
                          "peer no later than version 0x%02"PRIx8")",
                          vconn->name, vconn->version, vconn->min_version,
-                         OFP_VERSION, oh->version);
+                         OFP10_VERSION, oh->version);
                 vconn->state = VCS_CONNECTED;
             }
             ofpbuf_delete(b);
@@ -438,19 +456,15 @@ vcs_recv_hello(struct vconn *vconn)
 static void
 vcs_send_error(struct vconn *vconn)
 {
-    struct ofp_error_msg *error;
     struct ofpbuf *b;
     char s[128];
     int retval;
 
     snprintf(s, sizeof s, "We support versions 0x%02x to 0x%02x inclusive but "
              "you support no later than version 0x%02"PRIx8".",
-             vconn->min_version, OFP_VERSION, vconn->version);
-    error = make_openflow(sizeof *error, OFPT_ERROR, &b);
-    error->type = htons(OFPET_HELLO_FAILED);
-    error->code = htons(OFPHFC_INCOMPATIBLE);
-    ofpbuf_put(b, s, strlen(s));
-    update_openflow_length(b);
+             vconn->min_version, OFP10_VERSION, vconn->version);
+    b = ofperr_encode_hello(OFPERR_OFPHFC_INCOMPATIBLE,
+                            ofperr_domain_from_version(vconn->version), s);
     retval = do_send(vconn, b);
     if (retval) {
         ofpbuf_delete(b);
@@ -470,7 +484,7 @@ vconn_connect(struct vconn *vconn)
 {
     enum vconn_state last_state;
 
-    assert(vconn->min_version >= 0);
+    assert(vconn->min_version > 0);
     do {
         last_state = vconn->state;
         switch (vconn->state) {
@@ -537,14 +551,14 @@ do_recv(struct vconn *vconn, struct ofpbuf **msgp)
         }
 
         oh = ofpbuf_at_assert(*msgp, 0, sizeof *oh);
-        if (oh->version != vconn->version
+        if ((oh->version != vconn->version || oh->version == 0)
             && oh->type != OFPT_HELLO
             && oh->type != OFPT_ERROR
             && oh->type != OFPT_ECHO_REQUEST
             && oh->type != OFPT_ECHO_REPLY
             && oh->type != OFPT_VENDOR)
         {
-            if (vconn->version < 0) {
+            if (vconn->version == 0) {
                 VLOG_ERR_RL(&bad_ofmsg_rl,
                             "%s: received OpenFlow message type %"PRIu8" "
                             "before version negotiation complete",
@@ -648,10 +662,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;
 
@@ -667,7 +681,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);
     }
 }
@@ -677,12 +692,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;
@@ -693,6 +712,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. */
+    barrier = ofputil_encode_barrier_request();
+    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)
 {
@@ -784,7 +901,7 @@ pvconn_verify_name(const char *name)
  * stores a pointer to the new connection in '*pvconnp', otherwise a null
  * pointer.  */
 int
-pvconn_open(const char *name, struct pvconn **pvconnp)
+pvconn_open(const char *name, struct pvconn **pvconnp, uint8_t dscp)
 {
     struct pvconn_class *class;
     struct pvconn *pvconn;
@@ -801,7 +918,7 @@ pvconn_open(const char *name, struct pvconn **pvconnp)
 
     /* Call class's "open" function. */
     suffix_copy = xstrdup(strchr(name, ':') + 1);
-    error = class->listen(name, suffix_copy, &pvconn);
+    error = class->listen(name, suffix_copy, &pvconn, dscp);
     free(suffix_copy);
     if (error) {
         goto error;
@@ -841,7 +958,7 @@ pvconn_close(struct pvconn *pvconn)
  *
  * The new vconn will automatically negotiate an OpenFlow protocol version
  * acceptable to both peers on the connection.  The version negotiated will be
- * no lower than 'min_version' and no higher than OFP_VERSION.
+ * no lower than 'min_version' and no higher than OFP10_VERSION.
  *
  * pvconn_accept() will not block waiting for a connection.  If no connection
  * is ready to be accepted, it returns EAGAIN immediately. */
@@ -891,8 +1008,8 @@ vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
                     : !connect_status ? VCS_SEND_HELLO
                     : VCS_DISCONNECTED);
     vconn->error = connect_status;
-    vconn->version = -1;
-    vconn->min_version = -1;
+    vconn->version = 0;
+    vconn->min_version = 0;
     vconn->remote_ip = 0;
     vconn->remote_port = 0;
     vconn->local_ip = 0;
@@ -902,25 +1019,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)
+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)
+vconn_set_local_port(struct vconn *vconn, ovs_be16 port)
 {
     vconn->local_port = port;
 }