/*
- * 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.
#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);
*
* 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;
/* 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;
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);
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)
{
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 {
"(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);
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);
{
enum vconn_state last_state;
- assert(vconn->min_version >= 0);
+ assert(vconn->min_version > 0);
do {
last_state = vconn->state;
switch (vconn->state) {
}
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",
* 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,
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)
{
* 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;
/* 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;
*
* 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. */
: !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;