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 <crbar...@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(+)
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 */
_______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev