[ovs-dev] [PATCH] groups: Add 'ofpbucket' abstraction to ofp-actions library

Casey Barker crbarker at google.com
Fri Aug 9 23:09:19 UTC 2013


This is some early infrastructure towards support for Openflow forwarding
groups as introduced in the 1.1 spec.

The ofpbucket structure is an internal representation of a group bucket and
holds a list of ofpacts. This patch includes management functions for
parsing/packing buckets in Openflow and managing collections of buckets.

This code has been tested as part of a working implementation of groups, and
unit tested in a downstream harness, but lacks unit tests in the OVS
harness.

Signed-off-by: Casey Barker <crbarker at google.com>
---
Ben: Please feel free to accept or reject this, depending on how it fits
your plans for groups. In my implementation, this structure ends up passing
through the ofproto-provider API.

Patch attached to prevent gmail from eating it.

 lib/ofp-actions.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/ofp-actions.h |  61 +++++++++++++
 2 files changed, 303 insertions(+)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openvswitch.org/pipermail/ovs-dev/attachments/20130809/4e69ca17/attachment-0003.html>
-------------- next part --------------
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 61e2854..b1d68fa 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -2474,3 +2474,245 @@ ofpact_set_field_init(struct ofpact_reg_load *load, const struct mf_field *mf,
     bitwise_copy(src, mf->n_bytes, load->dst.ofs,
                  &load->subvalue, sizeof load->subvalue, 0, mf->n_bits);
 }
+
+/* Converting OpenFlow to ofpbuckets. */
+
+static enum ofperr
+ofpbucket_from_openflow11(const struct ofp11_bucket *b,
+                          struct ofpbuf *ofpbuckets)
+{
+    enum ofperr error;
+    struct ofpbuf ofpacts;
+    struct ofpbuf openflow_actions;
+    size_t actions_len = ntohs(b->len) - sizeof(*b);
+
+    struct ofpbucket *ofpbucket = ofpbucket_put(ofpbuckets);
+    ofpbucket->weight = ntohs(b->weight);
+    ofpbucket->watch_port = ntohl(b->watch_port);
+    ofpbucket->watch_group = ntohl(b->watch_group);
+
+    ofpbuf_init(&ofpacts, 64);
+    ofpbuf_use_const(&openflow_actions, b+1, actions_len);
+    error = ofpacts_pull_actions(&openflow_actions, actions_len, &ofpacts,
+                                 ofpacts_from_openflow11);
+
+    ofpbucket->ofpacts_len = ofpacts.size;
+    ofpbucket->ofpacts = ofpbuf_steal_data(&ofpacts);
+    return error;
+}
+
+static inline struct ofp11_bucket *
+bucket11_next(const struct ofp11_bucket *b)
+{
+    return (struct ofp11_bucket *) (void *) ((uint8_t *) b + ntohs(b->len));
+}
+
+static inline bool
+bucket11_is_valid(const struct ofp11_bucket *b, size_t buckets_len)
+{
+    uint16_t len;
+    if (buckets_len < sizeof *b) return false;
+    len = ntohs(b->len);
+    return (len >= sizeof *b && len <= buckets_len);
+}
+
+static void
+log_bad_bucket11(const struct ofp11_bucket *buckets, size_t buckets_len,
+                 size_t ofs, enum ofperr error)
+{
+    if (!VLOG_DROP_WARN(&rl)) {
+        struct ds s;
+
+        ds_init(&s);
+        ds_put_hex_dump(&s, buckets, buckets_len, 0, false);
+        VLOG_WARN("bad bucket at offset %#zx (%s):\n%s",
+                  ofs, ofperr_get_name(error), ds_cstr(&s));
+        ds_destroy(&s);
+    }
+}
+
+static enum ofperr
+ofpbuckets_from_openflow11(const void *openflow_buckets,
+                           size_t buckets_len,
+                           struct ofpbuf *ofpbuckets)
+{
+    const struct ofp11_bucket *buckets = openflow_buckets;
+    const struct ofp11_bucket *b;
+    size_t left;
+    enum ofperr error;
+
+    ofpbuf_clear(ofpbuckets);
+    for (b = buckets, left = buckets_len;
+         left > 0 && bucket11_is_valid(b, left);
+         left -= ntohs(b->len), b = bucket11_next(b)) {
+        error = ofpbucket_from_openflow11(b, ofpbuckets);
+        if (error) {
+            log_bad_bucket11(buckets, buckets_len, b - buckets, error);
+            return error;
+        }
+    }
+    if (left) {
+        error = OFPERR_OFPBAC_BAD_LEN;
+        log_bad_bucket11(buckets, buckets_len, b - buckets, error);
+        return error;
+    }
+
+    return 0;
+}
+
+static enum ofperr
+ofpbuckets_pull_openflow(struct ofpbuf *openflow,
+                         size_t buckets_len,
+                         struct ofpbucket **ofpbuckets,
+                         size_t *n_ofpbuckets,
+                         enum ofperr(*parser)(const void *openflow_buckets,
+                                              size_t buckets_len,
+                                              struct ofpbuf *ofpbuckets))
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+    struct ofp11_bucket *buckets;
+    struct ofpbuf buffer;
+    enum ofperr error;
+
+    buckets = ofpbuf_try_pull(openflow, buckets_len);
+    if (buckets == NULL) {
+        VLOG_WARN_RL(&rl, "OpenFlow message buckets length %zu exceeds "
+                     "remaining message length (%zu)",
+                     buckets_len, openflow->size);
+        *ofpbuckets = NULL;
+        *n_ofpbuckets = 0;
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    ofpbuf_init(&buffer, 64);
+    error = parser(buckets, buckets_len, &buffer);
+    *n_ofpbuckets = buffer.size / sizeof **ofpbuckets;
+    *ofpbuckets = ofpbuf_steal_data(&buffer);
+
+    if (error) {
+        ofpbucket_free(*ofpbuckets, *n_ofpbuckets);
+        *ofpbuckets = NULL;
+        *n_ofpbuckets = 0;
+    }
+    return error;
+}
+
+/* This call attempts to convert 'buckets_len' bytes of OpenFlow 1.1 buckets
+ * from the front of 'openflow' into ofpbuckets.  On success, it returns a
+ * buffer containing 'n_ofpbuckets' at '*ofpbuckets', and the caller owns the
+ * buckets and attached actions; on failure, it sets n_ofpbuckets to zero and
+ * *ofpbuckets to null.  It returns 0 if successful, otherwise an OpenFlow
+ * error. */
+enum ofperr
+ofpbuckets_pull_openflow11(struct ofpbuf *openflow,
+                           size_t buckets_len,
+                           struct ofpbucket **ofpbuckets,
+                           size_t *n_ofpbuckets)
+{
+    return ofpbuckets_pull_openflow(openflow,
+                                    buckets_len,
+                                    ofpbuckets,
+                                    n_ofpbuckets,
+                                    ofpbuckets_from_openflow11);
+}
+
+/* Converting ofpbuckets to OpenFlow. */
+
+static void
+ofpbucket_to_openflow11(const struct ofpbucket *b, struct ofpbuf *openflow)
+{
+    struct ofp11_bucket *ofb;
+    ofb = openflow->l3 = ofpbuf_put_zeros(openflow, sizeof *ofb);
+    ofb->weight = htons(b->weight);
+    ofb->watch_port = htonl(b->watch_port);
+    ofb->watch_group = htonl(b->watch_group);
+    ofpacts_put_openflow11_actions(b->ofpacts, b->ofpacts_len, openflow);
+
+    /* Inserting actions may have rebuffered the buckets. */
+    ofb = openflow->l3;
+    ofb->len = htons((uint8_t *)ofpbuf_tail(openflow) - (uint8_t *)ofb);
+}
+
+/* Converts the 'n_ofpbuckets' ofpbuckets in 'ofpbuckets' into
+ * OpenFlow 1.1 buckets, appending the buckets to any existing
+ * data in 'openflow'. */
+void
+ofpbuckets_put_openflow11(const struct ofpbucket ofpbuckets[],
+                          size_t n_ofpbuckets,
+                          struct ofpbuf *openflow)
+{
+    const struct ofpbucket *b;
+
+    OFPBUCKET_FOR_EACH (b, ofpbuckets, n_ofpbuckets) {
+        ofpbucket_to_openflow11(b, openflow);
+    }
+}
+
+void
+ofpbuckets_format(const struct ofpbucket *ofpbuckets,
+                  size_t n_ofpbuckets,
+                  struct ds *s)
+{
+    const struct ofpbucket *ofpbucket;
+    int i = 0;
+    OFPBUCKET_FOR_EACH (ofpbucket, ofpbuckets, n_ofpbuckets) {
+        ds_put_format(s, "bucket %d: ", i);
+        if(ofpbucket->weight) {
+            ds_put_format(s, "weight=%d,", ofpbucket->weight);
+        }
+        if(ofpbucket->watch_port) {
+            ds_put_format(s, "watch_port=%d,", ofpbucket->watch_port);
+        }
+        if(ofpbucket->watch_group) {
+            ds_put_format(s, "watch_group=%d,", ofpbucket->watch_group);
+        }
+        ofpacts_format(ofpbucket->ofpacts, ofpbucket->ofpacts_len, s);
+        ds_put_char(s, '\n');
+        i++;
+    }
+}
+
+struct ofpbucket *
+ofpbucket_put(struct ofpbuf *ofpbuckets)
+{
+  return ofpbuf_put_zeros(ofpbuckets, sizeof (struct ofpbucket));
+}
+
+void
+ofpbucket_pull(struct ofpbuf *buffer,
+               struct ofpbucket **ofpbuckets,
+               size_t *n_ofpbuckets)
+{
+    *n_ofpbuckets = buffer->size / sizeof **ofpbuckets;
+    *ofpbuckets = ofpbuf_steal_data(buffer);
+}
+
+struct ofpbucket *
+ofpbucket_clone(struct ofpbucket* ofpbuckets, size_t n_ofpbuckets) {
+    struct ofpbucket *old, *new, *clone;
+    size_t len = sizeof *clone * n_ofpbuckets;
+
+    clone = new = xmalloc(len);
+    memcpy(clone, ofpbuckets, len);
+
+    OFPBUCKET_FOR_EACH (old, ofpbuckets, n_ofpbuckets) {
+        new->ofpacts = xmalloc(old->ofpacts_len);
+        memcpy(new->ofpacts, old->ofpacts, old->ofpacts_len);
+        new++;
+    }
+    return clone;
+}
+
+void
+ofpbucket_free(struct ofpbucket* ofpbuckets, size_t n_ofpbuckets)
+{
+    struct ofpbucket *ofpbucket;
+    OFPBUCKET_FOR_EACH (ofpbucket, ofpbuckets, n_ofpbuckets) {
+        if(ofpbucket->ofpacts) {
+            free(ofpbucket->ofpacts);
+            ofpbucket->ofpacts = NULL;
+        }
+        ofpbucket->ofpacts_len = 0;
+    }
+    free(ofpbuckets);
+}
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index ca33ca8..0d0d7e6 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -25,6 +25,10 @@
 #include "openflow/nicira-ext.h"
 #include "openvswitch/types.h"
 
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
 /* List of OVS abstracted actions.
  *
  * This macro is used directly only internally by this header, but the list is
@@ -654,4 +658,61 @@ enum ovs_instruction_type ovs_instruction_type_from_ofpact_type(
 void ofpact_set_field_init(struct ofpact_reg_load *load,
                            const struct mf_field *mf, const void *src);
 
+/* Structure of an action bucket.
+ *
+ * Each bucket owns a collection of struct ofpacts representing one
+ * set of Openflow actions for a group.  The owner of the bucket also
+ * owns the attached ofpacts and must free them if necessary.
+ */
+struct ofpbucket {
+    uint16_t weight;            /* Relative weight for select groups. */
+    uint32_t watch_port;        /* Port whose state determines whether this
+                                 * bucket is alive for fast-failover. */
+    uint32_t watch_group;       /* Group whose state determines whether this
+                                 * bucket is alive for fast-failover. */
+    struct ofpact *ofpacts;     /* Actions associated with this bucket. */
+    size_t ofpacts_len;         /* Length of the actions. */
+};
+
+#define OFPBUCKET_FOR_EACH(POS, OFPBUCKETS, N_OFPBUCKETS)                  \
+    for ((POS) = (OFPBUCKETS); (POS) < ((OFPBUCKETS) + (N_OFPBUCKETS));    \
+         (POS) = (POS) + 1)
+
+/* Converting OpenFlow to ofpbuckets. */
+enum ofperr ofpbuckets_pull_openflow11(struct ofpbuf *openflow,
+                                       size_t buckets_len,
+                                       struct ofpbucket **ofpbuckets,
+                                       size_t *n_ofpbuckets);
+
+/* Converting ofpbuckets to OpenFlow. */
+void ofpbuckets_put_openflow11(const struct ofpbucket ofpbuckets[],
+                               size_t n_ofpbuckets,
+                               struct ofpbuf *openflow);
+
+/* Formatting ofpbuckets.  */
+void ofpbuckets_format(const struct ofpbucket *,
+                       size_t n_ofpbuckets,
+                       struct ds *s);
+
+/* Appends a new 'ofpbucket' to 'ofpbuckets' initializing fields to zero. */
+struct ofpbucket *ofpbucket_put(struct ofpbuf *ofpbuckets);
+
+/* Steals the data from 'buffer' and assigns it to 'ofpbuckets', computing
+ * 'n_ofpbuckets'. */
+void ofpbucket_pull(struct ofpbuf *buffer,
+                    struct ofpbucket **ofpbuckets,
+                    size_t *n_ofpbuckets);
+
+/* Clones ofpbuckets including all actions and returns a pointer to the
+ * clone. */
+struct ofpbucket *ofpbucket_clone(struct ofpbucket* ofpbuckets,
+                                  size_t n_ofpbuckets);
+
+/* Frees 'ofpbuckets->ofpacts' for all buckets in the array. */
+void ofpbucket_free(struct ofpbucket* ofpbuckets, size_t n_ofpbuckets);
+
+#ifdef  __cplusplus
+}
+#endif
+
 #endif /* ofp-actions.h */


More information about the dev mailing list