#include "poll-loop.h"
#include "random.h"
#include "util.h"
-
-#define THIS_MODULE VLM_vconn
#include "vlog.h"
+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 {
/* This is the ordinary progression of states. */
/* 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");
fatal_signal_run();
error = vconn_open(name, min_version, &vconn);
- while (error == EAGAIN) {
- vconn_run(vconn);
- vconn_run_wait(vconn);
- vconn_connect_wait(vconn);
- poll_block();
- error = vconn_connect(vconn);
+ if (!error) {
+ while ((error = vconn_connect(vconn)) == EAGAIN) {
+ vconn_run(vconn);
+ vconn_run_wait(vconn);
+ vconn_connect_wait(vconn);
+ poll_block();
+ }
assert(error != EINPROGRESS);
}
+
if (error) {
vconn_close(vconn);
*vconnp = NULL;
/* 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);
}
}
-/* 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)
{
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. */
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'.
*
* '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;
}
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);
}
}
* 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;
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)
{
}
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;
}