[ovs-dev] [PATCH v6] Add basic implementation for OpenFlow 1.4 bundles

Alexandru Copot alex.mihai.c at gmail.com
Fri May 2 06:54:27 UTC 2014


This is only the communication part of the bundles functionality.
The actual message pre-validation and commits are not implemented.

We also enable OF1.4 for all the tests.

Signed-off-by: Alexandru Copot <alex.mihai.c at gmail.com>
Cc: Daniel Baluta <dbaluta at ixiacom.com>
---
v6:
  * return error code for invalid inner message size
v5:
  * fold tests patch
  * add ofputil_encode_bundle_add()
  * remove duplicate header in OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE comment
  * return error on commit
  * check inner message size in ofputil_decode_bundle_add()
v4:
  * adjust copyright
  * fix style issues
  * remove all locking
  * delete bundles in ofconn_destroy()
v3:
  * rebase
  * adjusted to use ofpbuf_l3()
  * bug fixes
v2: rebase
---
 lib/learning-switch.c   |   2 +
 lib/ofp-errors.h        |  52 ++++++++++
 lib/ofp-msgs.h          |  10 ++
 lib/ofp-print.c         |  92 +++++++++++++++++
 lib/ofp-util.c          |  83 ++++++++++++++++
 lib/ofp-util.h          |  24 +++++
 lib/rconn.c             |   2 +
 ofproto/automake.mk     |   5 +-
 ofproto/bundles.c       | 256 ++++++++++++++++++++++++++++++++++++++++++++++++
 ofproto/bundles.h       |  49 +++++++++
 ofproto/connmgr.c       |  16 +++
 ofproto/connmgr.h       |   2 +
 ofproto/ofproto.c       |  70 +++++++++++++
 tests/ofp-print.at      | 125 +++++++++++++++++++++++
 tests/ofproto-macros.at |   4 +-
 tests/ofproto.at        | 234 +++++++++++++++++++++++++++++++++++++++++++
 16 files changed, 1023 insertions(+), 3 deletions(-)
 create mode 100644 ofproto/bundles.c
 create mode 100644 ofproto/bundles.h

diff --git a/lib/learning-switch.c b/lib/learning-switch.c
index c818a32..ca57911 100644
--- a/lib/learning-switch.c
+++ b/lib/learning-switch.c
@@ -398,6 +398,8 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
     case OFPTYPE_METER_FEATURES_STATS_REPLY:
     case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
     case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
+    case OFPTYPE_BUNDLE_CONTROL:
+    case OFPTYPE_BUNDLE_ADD_MESSAGE:
     default:
         if (VLOG_IS_DBG_ENABLED()) {
             char *s = ofp_to_string(ofpbuf_data(msg), ofpbuf_size(msg), 2);
diff --git a/lib/ofp-errors.h b/lib/ofp-errors.h
index c80a75e..b1bcf7c 100644
--- a/lib/ofp-errors.h
+++ b/lib/ofp-errors.h
@@ -557,6 +557,58 @@ enum ofperr {
     /* OF1.3+(13,5).  Permissions error. */
     OFPERR_OFPTFFC_EPERM,
 
+/* ## -------------------- ## */
+/* ## OFPET_BUNDLE_FAILED  ## */
+/* ## -------------------- ## */
+
+    /* OF1.4+(17,0).  Unspecified error. */
+    OFPERR_OFPBFC_UNKNOWN,
+
+    /* OF1.4+(17,1).  Permissions error. */
+    OFPERR_OFPBFC_EPERM,
+
+    /* OF1.4+(17,2).  Bundle ID doesn't exist. */
+    OFPERR_OFPBFC_BAD_ID,
+
+    /* OF1.4+(17,3).  Bundle ID already exists. */
+    OFPERR_OFPBFC_BUNDLE_EXIST,
+
+    /* OF1.4+(17,4).  Bundle ID is closed. */
+    OFPERR_OFPBFC_BUNDLE_CLOSED,
+
+    /* OF1.4+(17,5).  Too many bundle IDs. */
+    OFPERR_OFPBFC_OUT_OF_BUNDLES,
+
+    /* OF1.4+(17,6).  Unsupported of unknown message control type. */
+    OFPERR_OFPBFC_BAD_TYPE,
+
+    /* OF1.4+(17,7).  Unsupported, unknown, or inconsistent flags. */
+    OFPERR_OFPBFC_BAD_FLAGS,
+
+    /* OF1.4+(17,8).  Length problem in included message. */
+    OFPERR_OFPBFC_MSG_BAD_LEN,
+
+    /* OF1.4+(17,9).  Inconsistent or duplicate XID. */
+    OFPERR_OFPBFC_MSG_BAD_XID,
+
+    /* OF1.4+(17,10).  Unsupported message in this bundle. */
+    OFPERR_OFPBFC_MSG_UNSUP,
+
+    /* OF1.4+(17,11).  Unsupported message combination in this bundle. */
+    OFPERR_OFPBFC_MSG_CONFLICT,
+
+    /* OF1.4+(17,12).  Cant handle this many messages in bundle. */
+    OFPERR_OFPBFC_MSG_TOO_MANY,
+
+    /* OF1.4+(17,13).  One message in bundle failed. */
+    OFPERR_OFPBFC_MSG_FAILED,
+
+    /* OF1.4+(17,14).  Bundle is taking too long. */
+    OFPERR_OFPBFC_TIMEOUT,
+
+    /* OF1.4+(17,15).  Bundle is locking the resource. */
+    OFPERR_OFPBFC_BUNDLE_IN_PROGRESS,
+
 /* ## ------------------ ## */
 /* ## OFPET_EXPERIMENTER ## */
 /* ## ------------------ ## */
diff --git a/lib/ofp-msgs.h b/lib/ofp-msgs.h
index d8dee5b..573b4ce 100644
--- a/lib/ofp-msgs.h
+++ b/lib/ofp-msgs.h
@@ -233,6 +233,12 @@ enum ofpraw {
     /* OFPT 1.4+ (30): struct ofp14_role_status, uint8_t[8][]. */
     OFPRAW_OFPT14_ROLE_STATUS,
 
+    /* OFPT 1.4+ (33): struct ofp14_bundle_ctrl_msg, uint8_t[8][]. */
+    OFPRAW_OFPT14_BUNDLE_CONTROL,
+
+    /* OFPT 1.4+ (34): struct ofp14_bundle_add_msg, uint8_t[]. */
+    OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE,
+
 /* Standard statistics. */
 
     /* OFPST 1.0+ (0): void. */
@@ -508,6 +514,10 @@ enum ofptype {
     /* Controller role change event messages. */
     OFPTYPE_ROLE_STATUS,          /* OFPRAW_OFPT14_ROLE_STATUS. */
 
+    OFPTYPE_BUNDLE_CONTROL,       /* OFPRAW_OFPT14_BUNDLE_CONTROL. */
+
+    OFPTYPE_BUNDLE_ADD_MESSAGE,   /* OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE. */
+
     /* Statistics. */
     OFPTYPE_DESC_STATS_REQUEST,      /* OFPRAW_OFPST_DESC_REQUEST. */
     OFPTYPE_DESC_STATS_REPLY,        /* OFPRAW_OFPST_DESC_REPLY. */
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 38e228c..1dc634b 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -2668,6 +2668,90 @@ ofp_print_table_features(struct ds *s, const struct ofp_header *oh)
     }
 }
 
+static const char *
+bundle_flags_to_name(uint32_t bit)
+{
+    switch (bit) {
+    case OFPBF_ATOMIC:
+        return "atomic";
+    case OFPBF_ORDERED:
+        return "ordered";
+    default:
+        return NULL;
+    }
+}
+
+static void
+ofp_print_bundle_ctrl(struct ds *s, const struct ofp_header *oh)
+{
+    int error;
+    struct ofputil_bundle_ctrl_msg bctrl;
+
+    error = ofputil_decode_bundle_ctrl(oh, &bctrl);
+    if (error) {
+        ofp_print_error(s, error);
+        return;
+    }
+
+    ds_put_char(s, '\n');
+
+    ds_put_format(s, " bundle_id=%#"PRIx32" type=",  bctrl.bundle_id);
+    switch (bctrl.type) {
+    case OFPBCT_OPEN_REQUEST:
+        ds_put_cstr(s, "OPEN_REQUEST");
+        break;
+    case OFPBCT_OPEN_REPLY:
+        ds_put_cstr(s, "OPEN_REPLY");
+        break;
+    case OFPBCT_CLOSE_REQUEST:
+        ds_put_cstr(s, "CLOSE_REQUEST");
+        break;
+    case OFPBCT_CLOSE_REPLY:
+        ds_put_cstr(s, "CLOSE_REPLY");
+        break;
+    case OFPBCT_COMMIT_REQUEST:
+        ds_put_cstr(s, "COMMIT_REQUEST");
+        break;
+    case OFPBCT_COMMIT_REPLY:
+        ds_put_cstr(s, "COMMIT_REPLY");
+        break;
+    case OFPBCT_DISCARD_REQUEST:
+        ds_put_cstr(s, "DISCARD_REQUEST");
+        break;
+    case OFPBCT_DISCARD_REPLY:
+        ds_put_cstr(s, "DISCARD_REPLY");
+        break;
+    }
+
+    ds_put_cstr(s, " flags=");
+    ofp_print_bit_names(s, bctrl.flags, bundle_flags_to_name, ' ');
+}
+
+static void
+ofp_print_bundle_add(struct ds *s, const struct ofp_header *oh, int verbosity)
+{
+    int error;
+    struct ofputil_bundle_add_msg badd;
+    char *msg;
+
+    error = ofputil_decode_bundle_add(oh, &badd);
+    if (error) {
+        ofp_print_error(s, error);
+        return;
+    }
+
+    ds_put_char(s, '\n');
+    ds_put_format(s, " bundle_id=%#"PRIx32,  badd.bundle_id);
+    ds_put_cstr(s, " flags=");
+    ofp_print_bit_names(s, badd.flags, bundle_flags_to_name, ' ');
+
+    ds_put_char(s, '\n');
+    msg = ofp_to_string(badd.msg, badd.length, verbosity);
+    if (msg) {
+        ds_put_cstr(s, msg);
+    }
+}
+
 static void
 ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
                 struct ds *string, int verbosity)
@@ -2913,6 +2997,14 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
     case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
         ofp_print_nxst_flow_monitor_reply(string, msg);
         break;
+
+    case OFPTYPE_BUNDLE_CONTROL:
+        ofp_print_bundle_ctrl(string, msg);
+        break;
+
+    case OFPTYPE_BUNDLE_ADD_MESSAGE:
+        ofp_print_bundle_add(string, msg, verbosity);
+        break;
     }
 }
 
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 3484394..804cfb5 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -7054,3 +7054,86 @@ ofputil_append_queue_stat(struct list *replies,
         OVS_NOT_REACHED();
     }
 }
+
+enum ofperr
+ofputil_decode_bundle_ctrl(const struct ofp_header *oh,
+                           struct ofputil_bundle_ctrl_msg *msg)
+{
+    struct ofpbuf b;
+    enum ofpraw raw;
+    const struct ofp14_bundle_ctrl_msg *m;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+    ovs_assert(raw == OFPRAW_OFPT14_BUNDLE_CONTROL);
+
+    m = ofpbuf_l3(&b);
+    msg->bundle_id = ntohl(m->bundle_id);
+    msg->type = ntohs(m->type);
+    msg->flags = ntohs(m->flags);
+
+    return 0;
+}
+
+struct ofpbuf *
+ofputil_encode_bundle_ctrl_reply(const struct ofp_header *oh,
+                                 struct ofputil_bundle_ctrl_msg *msg)
+{
+    struct ofpbuf *buf;
+    struct ofp14_bundle_ctrl_msg *m;
+
+    buf = ofpraw_alloc_reply(OFPRAW_OFPT14_BUNDLE_CONTROL, oh, 0);
+    m = ofpbuf_put_zeros(buf, sizeof *m);
+
+    m->bundle_id = htonl(msg->bundle_id);
+    m->type = htons(msg->type);
+    m->flags = htons(msg->flags);
+
+    return buf;
+}
+
+enum ofperr
+ofputil_decode_bundle_add(const struct ofp_header *oh,
+                          struct ofputil_bundle_add_msg *msg)
+{
+    struct ofpbuf b;
+    enum ofpraw raw;
+    size_t inner_size;
+    const struct ofp14_bundle_add_msg *m;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+    ovs_assert(raw == OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE);
+
+    m = ofpbuf_l3(&b);
+    msg->bundle_id = ntohl(m->bundle_id);
+    msg->flags = ntohs(m->flags);
+    msg->length = ntohs(m->message.length);
+    msg->msg = &m->message;
+
+    inner_size = ntohs(oh->length) - sizeof *m;
+    if (ntohs(msg->msg->length) - sizeof *oh > inner_size) {
+        return OFPERR_OFPBFC_MSG_BAD_LEN;
+    }
+
+    return 0;
+}
+
+struct ofpbuf *
+ofputil_encode_bundle_add(enum ofp_version ofp_version,
+                          struct ofputil_bundle_add_msg *msg)
+{
+    struct ofpbuf *request;
+    struct ofp14_bundle_add_msg *m;
+
+    request = ofpraw_alloc(OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE, ofp_version, 0);
+    m = ofpbuf_put_zeros(request, sizeof *m);
+
+    m->bundle_id = htonl(msg->bundle_id);
+    m->flags = htons(msg->flags);
+
+    ofpbuf_put_zeros(request, msg->length - sizeof(*msg->msg));
+    memcpy(&m->message, msg->msg, msg->length);
+
+    return request;
+}
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index 7c28034..3f39b22 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -1137,4 +1137,28 @@ void ofputil_append_group_desc_reply(const struct ofputil_group_desc *,
                                      struct list *replies);
 struct ofpbuf *ofputil_encode_group_desc_request(enum ofp_version);
 
+struct ofputil_bundle_ctrl_msg {
+    uint32_t    bundle_id;
+    uint16_t    type;
+    uint16_t    flags;
+};
+
+struct ofputil_bundle_add_msg {
+    uint32_t            bundle_id;
+    uint16_t            flags;
+    const struct ofp_header   *msg;
+    uint16_t            length;
+};
+
+enum ofperr ofputil_decode_bundle_ctrl(const struct ofp_header *,
+                                       struct ofputil_bundle_ctrl_msg *);
+
+struct ofpbuf *ofputil_encode_bundle_ctrl_reply(const struct ofp_header *,
+                                                struct ofputil_bundle_ctrl_msg *);
+
+struct ofpbuf *ofputil_encode_bundle_add(enum ofp_version ofp_version,
+                                         struct ofputil_bundle_add_msg *msg);
+
+enum ofperr ofputil_decode_bundle_add(const struct ofp_header *,
+                                      struct ofputil_bundle_add_msg *);
 #endif /* ofp-util.h */
diff --git a/lib/rconn.c b/lib/rconn.c
index cb3cdd5..2c49ca8 100644
--- a/lib/rconn.c
+++ b/lib/rconn.c
@@ -1350,6 +1350,8 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_GROUP_FEATURES_STATS_REPLY:
     case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
     case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
+    case OFPTYPE_BUNDLE_CONTROL:
+    case OFPTYPE_BUNDLE_ADD_MESSAGE:
         return false;
 
     case OFPTYPE_PACKET_IN:
diff --git a/ofproto/automake.mk b/ofproto/automake.mk
index cbdbd6f..22c50d1 100644
--- a/ofproto/automake.mk
+++ b/ofproto/automake.mk
@@ -45,7 +45,10 @@ ofproto_libofproto_la_SOURCES = \
 	ofproto/pinsched.c \
 	ofproto/pinsched.h \
 	ofproto/tunnel.c \
-	ofproto/tunnel.h
+	ofproto/tunnel.h \
+	ofproto/bundles.c \
+	ofproto/bundles.h
+
 ofproto_libofproto_la_CPPFLAGS = $(AM_CPPFLAGS)
 ofproto_libofproto_la_CFLAGS = $(AM_CFLAGS)
 ofproto_libofproto_la_LIBADD = lib/libsflow.la
diff --git a/ofproto/bundles.c b/ofproto/bundles.c
new file mode 100644
index 0000000..8db3667
--- /dev/null
+++ b/ofproto/bundles.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2013, 2014 Alexandru Copot <alex.mihai.c at gmail.com>, with support from IXIA.
+ * Copyright (c) 2013, 2014 Daniel Baluta <dbaluta at ixiacom.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "coverage.h"
+#include "fail-open.h"
+#include "in-band.h"
+#include "odp-util.h"
+#include "ofp-actions.h"
+#include "ofp-msgs.h"
+#include "ofp-util.h"
+#include "ofpbuf.h"
+#include "ofproto-provider.h"
+#include "pinsched.h"
+#include "poll-loop.h"
+#include "pktbuf.h"
+#include "rconn.h"
+#include "shash.h"
+#include "simap.h"
+#include "stream.h"
+#include "timeval.h"
+#include "vconn.h"
+#include "vlog.h"
+
+#include "bundles.h"
+
+VLOG_DEFINE_THIS_MODULE(bundles);
+
+enum bundle_state {
+    BS_OPEN,
+    BS_CLOSED
+};
+
+struct ofp_bundle {
+    struct hmap_node  node;      /* In struct ofconn's "bundles" hmap. */
+    uint32_t          id;
+    uint16_t          flags;
+    enum bundle_state state;
+
+    /* List of 'struct bundle_message's */
+    struct list       msg_list;
+};
+
+struct bundle_message {
+    struct ofp_header *msg;
+    struct list       node;  /* Element in 'struct ofp_bundles's msg_list */
+};
+
+static uint32_t
+bundle_hash(uint32_t id)
+{
+    return hash_int(id, 0);
+}
+
+static struct ofp_bundle *
+ofp_bundle_find(struct hmap *bundles, uint32_t id)
+{
+    struct ofp_bundle *bundle;
+
+    HMAP_FOR_EACH_IN_BUCKET(bundle, node, bundle_hash(id), bundles) {
+        if (bundle->id == id) {
+            return bundle;
+        }
+    }
+
+    return NULL;
+}
+
+static struct ofp_bundle *
+ofp_bundle_create(uint32_t id, uint16_t flags)
+{
+    struct ofp_bundle *bundle;
+
+    bundle = xmalloc(sizeof(*bundle));
+
+    bundle->id = id;
+    bundle->flags = flags;
+
+    list_init(&bundle->msg_list);
+
+    return bundle;
+}
+
+static void
+ofp_bundle_remove(struct ofconn *ofconn, struct ofp_bundle *item)
+{
+    struct bundle_message *msg, *next;
+    struct hmap *bundles;
+
+    LIST_FOR_EACH_SAFE (msg, next, node, &item->msg_list) {
+        list_remove(&msg->node);
+        free(msg->msg);
+        free(msg);
+    }
+
+    bundles = ofconn_get_bundles(ofconn);
+    hmap_remove(bundles, &item->node);
+
+    free(item);
+}
+
+void
+ofp_bundle_remove_all(struct ofconn *ofconn)
+{
+    struct ofp_bundle *b, *next;
+    struct hmap *bundles;
+
+    bundles = ofconn_get_bundles(ofconn);
+
+    HMAP_FOR_EACH_SAFE (b, next, node, bundles) {
+        ofp_bundle_remove(ofconn, b);
+    }
+}
+
+enum ofperr
+ofp_bundle_open(struct ofconn *ofconn, uint32_t id, uint16_t flags)
+{
+    struct hmap *bundles;
+    struct ofp_bundle *bundle;
+
+    bundles = ofconn_get_bundles(ofconn);
+    bundle = ofp_bundle_find(bundles, id);
+
+    if (bundle) {
+        VLOG_INFO("Bundle %x already exists.", id);
+        ofp_bundle_remove(ofconn, bundle);
+
+        return OFPERR_OFPBFC_BAD_ID;
+    }
+
+    /* TODO: Check the limit of open bundles */
+
+    bundle = ofp_bundle_create(id, flags);
+    bundle->state = BS_OPEN;
+
+    bundles = ofconn_get_bundles(ofconn);
+    hmap_insert(bundles, &bundle->node, bundle_hash(id));
+
+    return 0;
+}
+
+enum ofperr
+ofp_bundle_close(struct ofconn *ofconn, uint32_t id, uint16_t flags)
+{
+    struct hmap *bundles;
+    struct ofp_bundle *bundle;
+
+    bundles = ofconn_get_bundles(ofconn);
+    bundle = ofp_bundle_find(bundles, id);
+
+    if (!bundle) {
+        return OFPERR_OFPBFC_BAD_ID;
+    }
+
+    if (bundle->state == BS_CLOSED) {
+        ofp_bundle_remove(ofconn, bundle);
+        return OFPERR_OFPBFC_BUNDLE_CLOSED;
+    }
+
+    if (bundle->flags != flags) {
+        ofp_bundle_remove(ofconn, bundle);
+        return OFPERR_OFPBFC_BAD_FLAGS;
+    }
+
+    bundle->state = BS_CLOSED;
+    return 0;
+}
+
+enum ofperr
+ofp_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags)
+{
+    struct hmap *bundles;
+    struct ofp_bundle *bundle;
+
+    bundles = ofconn_get_bundles(ofconn);
+    bundle = ofp_bundle_find(bundles, id);
+
+    if (!bundle) {
+        return OFPERR_OFPBFC_BAD_ID;
+    }
+    if (bundle->flags != flags) {
+        ofp_bundle_remove(ofconn, bundle);
+        return OFPERR_OFPBFC_BAD_FLAGS;
+    }
+
+    /* TODO: actual commit */
+
+    return OFPERR_OFPBFC_MSG_UNSUP;
+}
+
+enum ofperr
+ofp_bundle_discard(struct ofconn *ofconn, uint32_t id)
+{
+    struct hmap *bundles;
+    struct ofp_bundle *bundle;
+
+    bundles = ofconn_get_bundles(ofconn);
+    bundle = ofp_bundle_find(bundles, id);
+
+    if (!bundle) {
+        return OFPERR_OFPBFC_BAD_ID;
+    }
+
+    ofp_bundle_remove(ofconn, bundle);
+
+    return 0;
+}
+
+enum ofperr
+ofp_bundle_add_message(struct ofconn *ofconn, struct ofputil_bundle_add_msg *badd)
+{
+    struct ofp_header *msg;
+    struct hmap *bundles;
+    struct ofp_bundle *bundle;
+    struct bundle_message *bmsg;
+
+    bundles = ofconn_get_bundles(ofconn);
+    bundle = ofp_bundle_find(bundles, badd->bundle_id);
+
+    if (!bundle) {
+        bundle = ofp_bundle_create(badd->bundle_id, badd->flags);
+        bundle->state = BS_OPEN;
+
+        bundles = ofconn_get_bundles(ofconn);
+        hmap_insert(bundles, &bundle->node, bundle_hash(badd->bundle_id));
+    }
+
+    if (bundle->state == BS_CLOSED) {
+        ofp_bundle_remove(ofconn, bundle);
+        return OFPERR_OFPBFC_BUNDLE_CLOSED;
+    }
+
+    msg = xmemdup(badd->msg, badd->length);
+
+    bmsg = xmalloc(sizeof(*bmsg));
+    bmsg->msg = msg;
+
+    list_push_back(&bundle->msg_list, &bmsg->node);
+
+    return 0;
+}
diff --git a/ofproto/bundles.h b/ofproto/bundles.h
new file mode 100644
index 0000000..9a6dfa5
--- /dev/null
+++ b/ofproto/bundles.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013, 2014 Alexandru Copot <alex.mihai.c at gmail.com>, with support from IXIA.
+ * Copyright (c) 2013, 2014 Daniel Baluta <dbaluta at ixiacom.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BUNDLES_H
+#define BUNDLES_H 1
+
+#include <sys/types.h>
+
+#include "ofp-msgs.h"
+#include "connmgr.h"
+#include "ofp-util.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+
+enum ofperr ofp_bundle_open(struct ofconn *ofconn, uint32_t id, uint16_t flags);
+
+enum ofperr ofp_bundle_close(struct ofconn *ofconn, uint32_t id, uint16_t flags);
+
+enum ofperr ofp_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags);
+
+enum ofperr ofp_bundle_discard(struct ofconn *ofconn, uint32_t id);
+
+enum ofperr ofp_bundle_add_message(struct ofconn *ofconn,
+                                   struct ofputil_bundle_add_msg *badd);
+
+void ofp_bundle_remove_all(struct ofconn *ofconn);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
index 9a5167d..75d616c 100644
--- a/ofproto/connmgr.c
+++ b/ofproto/connmgr.c
@@ -41,6 +41,8 @@
 #include "vconn.h"
 #include "vlog.h"
 
+#include "bundles.h"
+
 VLOG_DEFINE_THIS_MODULE(connmgr);
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
@@ -129,6 +131,9 @@ struct ofconn {
      * contains an update event of type NXFME_ABBREV and false otherwise.. */
     struct list updates OVS_GUARDED_BY(ofproto_mutex);
     bool sent_abbrev_update OVS_GUARDED_BY(ofproto_mutex);
+
+    /* Active bundles. Contains "struct ofp_bundle"s. */
+    struct hmap bundles;
 };
 
 static struct ofconn *ofconn_create(struct connmgr *, struct rconn *,
@@ -1136,6 +1141,13 @@ ofconn_add_opgroup(struct ofconn *ofconn, struct list *ofconn_node)
 {
     list_push_back(&ofconn->opgroups, ofconn_node);
 }
+
+struct hmap *
+ofconn_get_bundles(struct ofconn *ofconn)
+{
+    return &ofconn->bundles;
+}
+
 
 /* Private ofconn functions. */
 
@@ -1163,6 +1175,8 @@ ofconn_create(struct connmgr *mgr, struct rconn *rconn, enum ofconn_type type,
     hmap_init(&ofconn->monitors);
     list_init(&ofconn->updates);
 
+    hmap_init(&ofconn->bundles);
+
     ofconn_flush(ofconn);
 
     return ofconn;
@@ -1263,6 +1277,8 @@ ofconn_destroy(struct ofconn *ofconn)
         hmap_remove(&ofconn->connmgr->controllers, &ofconn->hmap_node);
     }
 
+    ofp_bundle_remove_all(ofconn);
+
     hmap_destroy(&ofconn->monitors);
     list_remove(&ofconn->node);
     rconn_destroy(ofconn->rconn);
diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h
index 20c8160..7f69d4f 100644
--- a/ofproto/connmgr.h
+++ b/ofproto/connmgr.h
@@ -161,6 +161,8 @@ void ofconn_add_opgroup(struct ofconn *, struct list *);
 void ofconn_remove_opgroup(struct ofconn *, struct list *,
                            const struct ofp_header *request, int error);
 
+struct hmap *ofconn_get_bundles(struct ofconn *ofconn);
+
 /* Sending asynchronous messages. */
 bool connmgr_wants_packet_in_on_miss(struct connmgr *mgr);
 void connmgr_send_port_status(struct connmgr *, struct ofconn *source,
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 3d788a6..2776a6c 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -58,6 +58,7 @@
 #include "unaligned.h"
 #include "unixctl.h"
 #include "vlog.h"
+#include "bundles.h"
 
 VLOG_DEFINE_THIS_MODULE(ofproto);
 
@@ -5932,6 +5933,69 @@ handle_table_mod(struct ofconn *ofconn, const struct ofp_header *oh)
 }
 
 static enum ofperr
+handle_bundle_control(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    enum ofperr error;
+    struct ofputil_bundle_ctrl_msg bctrl;
+    struct ofpbuf *buf;
+    struct ofputil_bundle_ctrl_msg reply;
+
+    error = ofputil_decode_bundle_ctrl(oh, &bctrl);
+    if (error) {
+        return error;
+    }
+    reply.flags = 0;
+    reply.bundle_id = bctrl.bundle_id;
+
+    switch (bctrl.type) {
+        case OFPBCT_OPEN_REQUEST:
+        error = ofp_bundle_open(ofconn, bctrl.bundle_id, bctrl.flags);
+        reply.type = OFPBCT_OPEN_REPLY;
+        break;
+    case OFPBCT_CLOSE_REQUEST:
+        error = ofp_bundle_close(ofconn, bctrl.bundle_id, bctrl.flags);
+        reply.type = OFPBCT_CLOSE_REPLY;;
+        break;
+    case OFPBCT_COMMIT_REQUEST:
+        error = ofp_bundle_commit(ofconn, bctrl.bundle_id, bctrl.flags);
+        reply.type = OFPBCT_COMMIT_REPLY;
+        break;
+    case OFPBCT_DISCARD_REQUEST:
+        error = ofp_bundle_discard(ofconn, bctrl.bundle_id);
+        reply.type = OFPBCT_DISCARD_REPLY;
+        break;
+
+    case OFPBCT_OPEN_REPLY:
+    case OFPBCT_CLOSE_REPLY:
+    case OFPBCT_COMMIT_REPLY:
+    case OFPBCT_DISCARD_REPLY:
+        return OFPERR_OFPBFC_BAD_TYPE;
+        break;
+    }
+
+    if (!error) {
+        buf = ofputil_encode_bundle_ctrl_reply(oh, &reply);
+        ofconn_send_reply(ofconn, buf);
+    }
+    return error;
+}
+
+
+static enum ofperr
+handle_bundle_add(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    enum ofperr error;
+    struct ofputil_bundle_add_msg badd;
+
+    error = ofputil_decode_bundle_add(oh, &badd);
+    if (error) {
+        return error;
+    }
+
+    return ofp_bundle_add_message(ofconn, &badd);
+}
+
+static enum ofperr
 handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     OVS_EXCLUDED(ofproto_mutex)
 {
@@ -6063,6 +6127,12 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
         return handle_queue_get_config_request(ofconn, oh);
 
+    case OFPTYPE_BUNDLE_CONTROL:
+        return handle_bundle_control(ofconn, oh);
+
+    case OFPTYPE_BUNDLE_ADD_MESSAGE:
+        return handle_bundle_add(ofconn, oh);
+
     case OFPTYPE_HELLO:
     case OFPTYPE_ERROR:
     case OFPTYPE_FEATURES_REPLY:
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index b74b681..ba1abf1 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -2807,3 +2807,128 @@ NXST_FLOW_MONITOR reply (xid=0x4):
  event=ABBREV xid=0x186a0
 ])
 AT_CLEANUP
+
+
+AT_SETUP([OFPT_BUNDLE_CONTROL - OPEN_REQUEST])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 21 00 10 00 00 00 00 \
+00 00 00 01 00 00 00 01 \
+"], [0], [dnl
+OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0):
+ bundle_id=0x1 type=OPEN_REQUEST flags=atomic
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BUNDLE_CONTROL - OPEN_REQUEST])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 21 00 10 00 00 00 00 \
+00 00 00 01 00 00 00 02 \
+"], [0], [dnl
+OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0):
+ bundle_id=0x1 type=OPEN_REQUEST flags=ordered
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BUNDLE_CONTROL - OPEN_REQUEST])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 21 00 10 00 00 00 00 \
+00 00 00 01 00 00 00 03 \
+"], [0], [dnl
+OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0):
+ bundle_id=0x1 type=OPEN_REQUEST flags=atomic ordered
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BUNDLE_CONTROL - OPEN_REPLY])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 21 00 10 00 00 00 00 \
+00 00 00 01 00 01 00 01 \
+"], [0], [dnl
+OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0):
+ bundle_id=0x1 type=OPEN_REPLY flags=atomic
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BUNDLE_CONTROL - CLOSE_REQUEST])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 21 00 10 00 00 00 00 \
+00 00 00 01 00 02 00 01 \
+"], [0], [dnl
+OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0):
+ bundle_id=0x1 type=CLOSE_REQUEST flags=atomic
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BUNDLE_CONTROL - CLOSE_REPLY])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 21 00 10 00 00 00 00 \
+00 00 00 01 00 03 00 01 \
+"], [0], [dnl
+OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0):
+ bundle_id=0x1 type=CLOSE_REPLY flags=atomic
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BUNDLE_CONTROL - COMMIT_REQUEST])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 21 00 10 00 00 00 00 \
+00 00 00 01 00 04 00 01 \
+"], [0], [dnl
+OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0):
+ bundle_id=0x1 type=COMMIT_REQUEST flags=atomic
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BUNDLE_CONTROL - COMMIT_REPLY])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 21 00 10 00 00 00 00 \
+00 00 00 01 00 05 00 01 \
+"], [0], [dnl
+OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0):
+ bundle_id=0x1 type=COMMIT_REPLY flags=atomic
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BUNDLE_CONTROL - DISCARD_REQUEST])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 21 00 10 00 00 00 00 \
+00 00 00 01 00 06 00 01 \
+"], [0], [dnl
+OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0):
+ bundle_id=0x1 type=DISCARD_REQUEST flags=atomic
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BUNDLE_CONTROL - DISCARD_REPLY])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 21 00 10 00 00 00 00 \
+00 00 00 01 00 07 00 01 \
+"], [0], [dnl
+OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0):
+ bundle_id=0x1 type=DISCARD_REPLY flags=atomic
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BUNDLE_ADD_MESSAGE - OFPT_HELLO])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 22 00 20 00 00 00 00 \
+00 00 00 01 00 01 00 01 02 00 00 08 00 00 00 00 \
+00 00 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPT_BUNDLE_ADD_MESSAGE (OF1.4) (xid=0x0):
+ bundle_id=0x1 flags=atomic
+OFPT_HELLO (OF1.1) (xid=0x0):
+ version bitmap: 0x01, 0x02
+])
+AT_CLEANUP
diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
index fe765ee..8a058ae 100644
--- a/tests/ofproto-macros.at
+++ b/tests/ofproto-macros.at
@@ -73,7 +73,7 @@ m4_define([OVS_VSWITCHD_START],
    AT_CHECK([ovs-vsctl --no-wait init])
 
    dnl Start ovs-vswitchd.
-   AT_CHECK([ovs-vswitchd --detach --no-chdir --pidfile --enable-dummy$3 --disable-system --log-file -vvconn -vofproto_dpif], [0], [], [stderr])
+   AT_CHECK([ovs-vswitchd --detach --no-chdir --pidfile --enable-dummy$3 --disable-system --log-file -vvconn -vofproto_dpif --enable-of14], [0], [], [stderr])
    AT_CAPTURE_FILE([ovs-vswitchd.log])
    AT_CHECK([[sed < stderr '
 /vlog|INFO|opened log file/d
@@ -83,7 +83,7 @@ m4_define([OVS_VSWITCHD_START],
 /ofproto|INFO|datapath ID changed to fedcba9876543210/d']])
 
    dnl Add bridges, ports, etc.
-   AT_CHECK([ovs-vsctl -- add-br br0 -- set bridge br0 datapath-type=dummy other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00 fail-mode=secure -- $1 m4_if([$2], [], [], [| ${PERL} $srcdir/uuidfilt.pl])], [0], [$2])
+   AT_CHECK([ovs-vsctl -- add-br br0 -- set bridge br0 datapath-type=dummy other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00 protocols=[[OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13,OpenFlow14]] fail-mode=secure -- $1 m4_if([$2], [], [], [| ${PERL} $srcdir/uuidfilt.pl])], [0], [$2])
 ])
 
 m4_divert_push([PREPARE_TESTS])
diff --git a/tests/ofproto.at b/tests/ofproto.at
index dfa8915..2ffe653 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -2329,3 +2329,237 @@ for pre in      '1 2' '[[]] 2' '1 [[]]' '[[]] [[]]' '2 1' '[[]] 1' '2 [[]]' \
 done
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+
+AT_SETUP([ofproto - bundles, open (OpenFlow 1.4)])
+AT_KEYWORDS([monitor])
+OVS_VSWITCHD_START
+
+# Start a monitor, use the required protocol version
+ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1
+AT_CAPTURE_FILE([monitor.log])
+
+# Send an OpenFlow14 message (05), OFPT_BUNDLE_CONTROL (21), length (10), xid (0a)
+ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 00 00 01"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([ofctl_strip < monitor.log], [], [dnl
+send: OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=OPEN_REQUEST flags=atomic
+OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=OPEN_REPLY flags=0
+OFPT_BARRIER_REPLY (OF1.4):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - bundles, double open (OpenFlow 1.4)])
+AT_KEYWORDS([monitor])
+OVS_VSWITCHD_START
+
+# Start a monitor, use the required protocol version
+ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1
+AT_CAPTURE_FILE([monitor.log])
+
+# Send twice an OpenFlow14 message (05), OFPT_BUNDLE_CONTROL (21), length (10), xid (0a)
+ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 00 00 01"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 00 00 01"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([ofctl_strip < monitor.log], [0], [dnl
+send: OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=OPEN_REQUEST flags=atomic
+OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=OPEN_REPLY flags=0
+OFPT_BARRIER_REPLY (OF1.4):
+send: OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=OPEN_REQUEST flags=atomic
+OFPT_ERROR (OF1.4): OFPBFC_BAD_ID
+OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=OPEN_REQUEST flags=atomic
+OFPT_BARRIER_REPLY (OF1.4):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - bundle close without open (OpenFlow 1.4)])
+AT_KEYWORDS([monitor])
+OVS_VSWITCHD_START
+
+# Start a monitor, use the required protocol version
+ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1
+AT_CAPTURE_FILE([monitor.log])
+
+ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 02 00 01"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([ofctl_strip < monitor.log], [0], [dnl
+send: OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=CLOSE_REQUEST flags=atomic
+OFPT_ERROR (OF1.4): OFPBFC_BAD_ID
+OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=CLOSE_REQUEST flags=atomic
+OFPT_BARRIER_REPLY (OF1.4):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - bundle double close (OpenFlow 1.4)])
+AT_KEYWORDS([monitor])
+OVS_VSWITCHD_START
+
+# Start a monitor, use the required protocol version
+ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1
+AT_CAPTURE_FILE([monitor.log])
+
+# Open, Close, Close
+ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 00 00 01"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 02 00 01"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 02 00 01"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([ofctl_strip < monitor.log], [0], [dnl
+send: OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=OPEN_REQUEST flags=atomic
+OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=OPEN_REPLY flags=0
+OFPT_BARRIER_REPLY (OF1.4):
+send: OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=CLOSE_REQUEST flags=atomic
+OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=CLOSE_REPLY flags=0
+OFPT_BARRIER_REPLY (OF1.4):
+send: OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=CLOSE_REQUEST flags=atomic
+OFPT_ERROR (OF1.4): OFPBFC_BUNDLE_CLOSED
+OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=CLOSE_REQUEST flags=atomic
+OFPT_BARRIER_REPLY (OF1.4):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - bundle close, different flags (OpenFlow 1.4)])
+AT_KEYWORDS([monitor])
+OVS_VSWITCHD_START
+
+# Start a monitor, use the required protocol version
+ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1
+AT_CAPTURE_FILE([monitor.log])
+
+# Open, Close, Close
+ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 00 00 01"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 02 00 02"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([ofctl_strip < monitor.log], [0], [dnl
+send: OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=OPEN_REQUEST flags=atomic
+OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=OPEN_REPLY flags=0
+OFPT_BARRIER_REPLY (OF1.4):
+send: OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=CLOSE_REQUEST flags=ordered
+OFPT_ERROR (OF1.4): OFPBFC_BAD_FLAGS
+OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=CLOSE_REQUEST flags=ordered
+OFPT_BARRIER_REPLY (OF1.4):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - bundle commit without open (OpenFlow 1.4)])
+AT_KEYWORDS([monitor])
+OVS_VSWITCHD_START
+
+# Start a monitor, use the required protocol version
+ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1
+AT_CAPTURE_FILE([monitor.log])
+
+# Open, Close, Close
+ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 04 00 01"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([ofctl_strip < monitor.log], [0], [dnl
+send: OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=COMMIT_REQUEST flags=atomic
+OFPT_ERROR (OF1.4): OFPBFC_BAD_ID
+OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=COMMIT_REQUEST flags=atomic
+OFPT_BARRIER_REPLY (OF1.4):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - bundle commit, different flags (OpenFlow 1.4)])
+AT_KEYWORDS([monitor])
+OVS_VSWITCHD_START
+
+# Start a monitor, use the required protocol version
+ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1
+AT_CAPTURE_FILE([monitor.log])
+
+# Open, Close, Close
+ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 00 00 01"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 04 00 02"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([ofctl_strip < monitor.log], [0], [dnl
+send: OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=OPEN_REQUEST flags=atomic
+OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=OPEN_REPLY flags=0
+OFPT_BARRIER_REPLY (OF1.4):
+send: OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=COMMIT_REQUEST flags=ordered
+OFPT_ERROR (OF1.4): OFPBFC_BAD_FLAGS
+OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=COMMIT_REQUEST flags=ordered
+OFPT_BARRIER_REPLY (OF1.4):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - bundle discard without open (OpenFlow 1.4)])
+AT_KEYWORDS([monitor])
+OVS_VSWITCHD_START
+
+# Start a monitor, use the required protocol version
+ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1
+AT_CAPTURE_FILE([monitor.log])
+
+# Open, Close, Close
+ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 06 00 01"
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([ofctl_strip < monitor.log], [0], [dnl
+send: OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=DISCARD_REQUEST flags=atomic
+OFPT_ERROR (OF1.4): OFPBFC_BAD_ID
+OFPT_BUNDLE_CONTROL (OF1.4):
+ bundle_id=0x1 type=DISCARD_REQUEST flags=atomic
+OFPT_BARRIER_REPLY (OF1.4):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
-- 
1.9.2




More information about the dev mailing list