[ovs-dev] [PATCH/RFC v2 3/8] Support decoding of NMX selection method

Simon Horman simon.horman at netronome.com
Fri Jan 30 02:41:51 UTC 2015


This is in preparation for supporting group mod and desc reply
messages with an NMX selection method group experimenter property.

NMX selection method
Signed-off-by: Simon Horman <simon.horman at netronome.com>

---
v2 Use list of struct field_array of TLVs rather than OF1.1 match
   for fields field of NMX selection method property
---
 lib/meta-flow.c            |  26 +++++
 lib/meta-flow.h            |  13 +++
 lib/nx-match.c             |  51 ++++++++++
 lib/nx-match.h             |   3 +
 lib/ofp-parse.c            |  15 ++-
 lib/ofp-print.c            |   4 +-
 lib/ofp-util.c             | 242 +++++++++++++++++++++++++++++++++++++++++++--
 lib/ofp-util.h             |  15 +++
 ofproto/ofproto-provider.h |   5 +
 ofproto/ofproto.c          |   9 ++
 utilities/ovs-ofctl.c      |   4 +-
 11 files changed, 372 insertions(+), 15 deletions(-)

diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 9ce4cfe..bd60cb6 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -2242,3 +2242,29 @@ mf_format_subvalue(const union mf_subvalue *subvalue, struct ds *s)
     }
     ds_put_char(s, '0');
 }
+
+void
+field_array_push_back(enum mf_field_id id, const union mf_value *value,
+                      struct ovs_list *field_array)
+{
+    struct field_array *fa;
+
+    fa = xmalloc(sizeof *fa);
+
+    fa->id = id;
+    fa->value = *value;
+
+    list_init(&fa->list_node);
+    list_push_back(field_array, &fa->list_node);
+}
+
+void
+field_array_delete(struct ovs_list *field_array)
+{
+    struct field_array *fa, *next;
+
+    LIST_FOR_EACH_SAFE (fa, next, list_node, field_array) {
+        list_remove(&fa->list_node);
+        free(fa);
+    }
+}
diff --git a/lib/meta-flow.h b/lib/meta-flow.h
index 4a6c443..a5716da 100644
--- a/lib/meta-flow.h
+++ b/lib/meta-flow.h
@@ -22,6 +22,7 @@
 #include <netinet/ip6.h>
 #include "bitmap.h"
 #include "flow.h"
+#include "list.h"
 #include "ofp-errors.h"
 #include "packets.h"
 #include "util.h"
@@ -1546,6 +1547,13 @@ union mf_subvalue {
 };
 BUILD_ASSERT_DECL(sizeof(union mf_value) == sizeof (union mf_subvalue));
 
+/* An array of fields with values */
+struct field_array {
+    struct ovs_list list_node;  /* List of other elements in the array */
+    enum mf_field_id id;        /* MFF_*. */
+    union mf_value value;
+};
+
 /* Finding mf_fields. */
 const struct mf_field *mf_from_name(const char *name);
 
@@ -1624,4 +1632,9 @@ void mf_format(const struct mf_field *,
                struct ds *);
 void mf_format_subvalue(const union mf_subvalue *subvalue, struct ds *s);
 
+/* Field Arrays. */
+void field_array_push_back(enum mf_field_id id, const union mf_value *,
+                           struct ovs_list *);
+void field_array_delete(struct ovs_list *);
+
 #endif /* meta-flow.h */
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 114c35b..4f84619 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -589,6 +589,57 @@ oxm_pull_match_loose(struct ofpbuf *b, struct match *match)
 {
     return oxm_pull_match__(b, false, match);
 }
+
+/* Verify an array of OXM TLVs treating value of each TLV as a mask,
+ * disallowing masks in each TLV and ignoring pre-requisites. */
+enum ofperr
+oxm_pull_field_array(const void *fields_data, size_t fields_len,
+                     struct ovs_list *field_array)
+{
+    struct ofpbuf b;
+    struct mf_bitmap used = MF_BITMAP_INITIALIZER;
+
+    ofpbuf_use_const(&b, fields_data, fields_len);
+    while (ofpbuf_size(&b)) {
+        const uint8_t *pos = ofpbuf_data(&b);
+        const struct mf_field *field;
+        union mf_value value, mask;
+        enum ofperr error;
+
+        error = nx_pull_match_entry(&b, false, &field, &value, &mask);
+        if (error) {
+            VLOG_DBG_RL(&rl, "error pulling field array field");
+            return error;
+        } else if (!field) {
+            VLOG_DBG_RL(&rl, "unknown field array field");
+            error = OFPERR_OFPBMC_BAD_FIELD;
+        } else if (bitmap_is_set(used.bm, field->id)) {
+            VLOG_DBG_RL(&rl, "duplicate field array field '%s'", field->name);
+            error = OFPERR_OFPBMC_DUP_FIELD;
+        } else if (!mf_is_mask_valid(field, &value)) {
+            VLOG_DBG_RL(&rl, "bad mask in field array field '%s'", field->name);
+            return OFPERR_OFPBMC_BAD_MASK;
+        } else if (!is_all_ones(&mask, field->n_bytes)) {
+            VLOG_DBG_RL(&rl, "mask has a mask in field array field '%s'",
+                        field->name);
+            return OFPERR_OFPBMC_BAD_VALUE;
+        } else {
+            bitmap_set1(used.bm, field->id);
+            field_array_push_back(field->id, &value, field_array);
+        }
+
+        if (error) {
+            const uint8_t *start = fields_data;
+
+            VLOG_DBG_RL(&rl, "error parsing OXM at offset %"PRIdPTR" "
+                        "within field array (%s)", pos - start,
+                        ofperr_to_string(error));
+            return error;
+        }
+    }
+
+    return 0;
+}
 
 /* nx_put_match() and helpers.
  *
diff --git a/lib/nx-match.h b/lib/nx-match.h
index 9cb6461..1c9821c 100644
--- a/lib/nx-match.h
+++ b/lib/nx-match.h
@@ -32,6 +32,7 @@ struct ofpact_reg_move;
 struct ofpact_reg_load;
 struct ofpact_stack;
 struct ofpbuf;
+struct ovs_list;
 struct nx_action_reg_load;
 struct nx_action_reg_move;
 
@@ -55,6 +56,8 @@ enum ofperr nx_pull_match_loose(struct ofpbuf *, unsigned int match_len,
                                 ovs_be64 *cookie_mask);
 enum ofperr oxm_pull_match(struct ofpbuf *, struct match *);
 enum ofperr oxm_pull_match_loose(struct ofpbuf *, struct match *);
+enum ofperr oxm_pull_field_array(const void *, size_t fields_len,
+                                 struct ovs_list *);
 int nx_put_match(struct ofpbuf *, const struct match *,
                  ovs_be64 cookie, ovs_be64 cookie_mask);
 int oxm_put_match(struct ofpbuf *, const struct match *, enum ofp_version);
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 9a5df3b..d033382 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -1209,6 +1209,7 @@ parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command,
     gm->group_id = OFPG_ANY;
     gm->command_bucket_id = OFPG15_BUCKET_ALL;
     list_init(&gm->buckets);
+    list_init(&gm->props.fields);
     if (command == OFPGC11_DELETE && string[0] == '\0') {
         gm->group_id = OFPG_ALL;
         return NULL;
@@ -1365,7 +1366,7 @@ parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command,
 
     return NULL;
  out:
-    ofputil_bucket_list_destroy(&gm->buckets);
+    ofputil_uninit_group_mod(gm);
     return error;
 }
 
@@ -1380,7 +1381,7 @@ parse_ofp_group_mod_str(struct ofputil_group_mod *gm, uint16_t command,
     free(string);
 
     if (error) {
-        ofputil_bucket_list_destroy(&gm->buckets);
+        ofputil_uninit_group_mod(gm);
     }
     return error;
 }
@@ -1419,6 +1420,9 @@ parse_ofp_group_mod_file(const char *file_name, uint16_t command,
                 if (list_is_empty(&(*gms)[i].buckets)) {
                     (*gms)[i].buckets.next = NULL;
                 }
+                if (list_is_empty(&(*gms)[i].props.fields)) {
+                    (*gms)[i].props.fields.next = NULL;
+                }
             }
             *gms = x2nrealloc(*gms, &allocated_gms, sizeof **gms);
             for (i = 0; i < *n_gms; i++) {
@@ -1427,6 +1431,11 @@ parse_ofp_group_mod_file(const char *file_name, uint16_t command,
                 } else {
                     list_init(&(*gms)[i].buckets);
                 }
+                if ((*gms)[i].props.fields.next) {
+                    list_moved(&(*gms)[i].props.fields);
+                } else {
+                    list_init(&(*gms)[i].props.fields);
+                }
             }
         }
         error = parse_ofp_group_mod_str(&(*gms)[*n_gms], command, ds_cstr(&s),
@@ -1435,7 +1444,7 @@ parse_ofp_group_mod_file(const char *file_name, uint16_t command,
             size_t i;
 
             for (i = 0; i < *n_gms; i++) {
-                ofputil_bucket_list_destroy(&(*gms)[i].buckets);
+                ofputil_uninit_group_mod(*gms + i);
             }
             free(*gms);
             *gms = NULL;
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index f4c5bc6..a596979 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -2219,7 +2219,7 @@ ofp_print_group_desc(struct ds *s, const struct ofp_header *oh)
         ds_put_char(s, ' ');
         ofp_print_group(s, gd.group_id, gd.type, &gd.buckets, oh->version,
                         false);
-        ofputil_bucket_list_destroy(&gd.buckets);
+        ofputil_uninit_group_desc(&gd);
      }
 }
 
@@ -2373,7 +2373,7 @@ ofp_print_group_mod(struct ds *s, const struct ofp_header *oh)
 
     ofp_print_group(s, gm.group_id, gm.type, &gm.buckets, oh->version,
                     bucket_command);
-    ofputil_bucket_list_destroy(&gm.buckets);
+    ofputil_uninit_group_mod(&gm);
 }
 
 static void
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index bf55fb2..9019a258 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -38,6 +38,7 @@
 #include "ofp-msgs.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
+#include "openflow/netronome-ext.h"
 #include "packets.h"
 #include "random.h"
 #include "unaligned.h"
@@ -59,6 +60,14 @@ struct ofp_prop_header {
     ovs_be16 len;
 };
 
+struct ofp_prop_experimenter {
+    ovs_be16 type;          /* OFP*_EXPERIMENTER. */
+    ovs_be16 length;        /* Length in bytes of this property. */
+    ovs_be32 experimenter;  /* Experimenter ID which takes the same form as
+                             * in struct ofp_experimenter_header. */
+    ovs_be32 exp_type;      /* Experimenter defined. */
+};
+
 /* Pulls a property, beginning with struct ofp_prop_header, from the beginning
  * of 'msg'.  Stores the type of the property in '*typep' and, if 'property' is
  * nonnull, the entire property, including the header, in '*property'.  Returns
@@ -7014,6 +7023,13 @@ ofputil_encode_group_stats_request(enum ofp_version ofp_version,
     return request;
 }
 
+void
+ofputil_uninit_group_desc(struct ofputil_group_desc *gd)
+{
+    ofputil_bucket_list_destroy(&gd->buckets);
+    field_array_delete(&gd->props.fields);
+}
+
 /* Decodes the OpenFlow group description request in 'oh', returning the group
  * whose description is requested, or OFPG_ALL if stats for all groups was
  * requested. */
@@ -7716,6 +7732,191 @@ ofputil_pull_ofp15_buckets(struct ofpbuf *msg, size_t buckets_length,
     return 0;
 }
 
+static void
+ofputil_init_group_properties(struct ofputil_group_props *gp)
+{
+    memset(gp, 0, sizeof *gp);
+    list_init(&gp->fields);
+}
+
+static enum ofperr
+parse_group_prop_nmx_selection_method(struct ofpbuf *payload,
+                                      enum ofp11_group_type group_type,
+                                      enum ofp15_group_mod_command group_cmd,
+                                      struct ofputil_group_props *gp)
+{
+    struct nmx_group_prop_selection_method *prop = ofpbuf_data(payload);
+    size_t fields_len, method_len;
+    enum ofperr error;
+
+    switch (group_type) {
+    case OFPGT11_SELECT:
+        break;
+    case OFPGT11_ALL:
+    case OFPGT11_INDIRECT:
+    case OFPGT11_FF:
+        log_property(false, "nmx selection method property is only allowed "
+                     "for select groups");
+        return OFPERR_OFPBPC_BAD_VALUE;
+    default:
+        OVS_NOT_REACHED();
+    }
+
+    switch (group_cmd) {
+    case OFPGC15_ADD:
+    case OFPGC15_MODIFY:
+        break;
+    case OFPGC15_DELETE:
+    case OFPGC15_INSERT_BUCKET:
+    case OFPGC15_REMOVE_BUCKET:
+        log_property(false, "nmx selection method property is only allowed "
+                     "for add and delete group modifications");
+        return OFPERR_OFPBPC_BAD_VALUE;
+    default:
+        OVS_NOT_REACHED();
+    }
+
+    if (ofpbuf_size(payload) < sizeof *prop) {
+        log_property(false, "nmx selection method property length "
+                     "%u is not valid", ofpbuf_size(payload));
+        return OFPERR_OFPBPC_BAD_LEN;
+    }
+
+    method_len = strnlen(prop->selection_method, NMX_MAX_SELECTION_METHOD_LEN);
+
+    if (method_len == NMX_MAX_SELECTION_METHOD_LEN) {
+        log_property(false, "nmx selection method is not null terminated");
+        return OFPERR_OFPBPC_BAD_VALUE;
+    }
+
+    strcpy(gp->selection_method, prop->selection_method);
+    gp->selection_method_param = ntohll(prop->selection_method_param);
+
+    if (!method_len && gp->selection_method_param) {
+        log_property(false, "nmx selection method parameter is non-zero but "
+                     "selection method is empty");
+        return OFPERR_OFPBPC_BAD_VALUE;
+    }
+
+    ofpbuf_pull(payload, sizeof *prop);
+
+    fields_len = ntohs(prop->length) - sizeof *prop;
+    if (!method_len && fields_len) {
+        log_property(false, "nmx selection method parameter is zero "
+                     "but fields are provided");
+        return OFPERR_OFPBPC_BAD_VALUE;
+    }
+
+    error = oxm_pull_field_array(ofpbuf_data(payload), fields_len,
+                                 &gp->fields);
+    if (error) {
+        log_property(false, "nmx selection method fields are invalid");
+        return error;
+    }
+
+    return 0;
+}
+
+static enum ofperr
+parse_group_prop_nmx(struct ofpbuf *payload, uint32_t exp_type,
+                     enum ofp11_group_type group_type,
+                     enum ofp15_group_mod_command group_cmd,
+                     struct ofputil_group_props *gp)
+{
+    enum ofperr error;
+
+    switch (exp_type) {
+    case NMXT_SELECTION_METHOD:
+        error = parse_group_prop_nmx_selection_method(payload, group_type,
+                                                      group_cmd, gp);
+        break;
+
+    default:
+        log_property(false, "unknown group property nmx experimenter type "
+                     "%"PRIu32, exp_type);
+        error = OFPERR_OFPBPC_BAD_TYPE;
+        break;
+    }
+
+    return error;
+}
+
+static enum ofperr
+parse_ofp15_group_prop_exp(struct ofpbuf *payload,
+                           enum ofp11_group_type group_type,
+                           enum ofp15_group_mod_command group_cmd,
+                           struct ofputil_group_props *gp)
+{
+    struct ofp_prop_experimenter *prop = ofpbuf_data(payload);
+    uint16_t experimenter;
+    uint32_t exp_type;
+    enum ofperr error;
+
+    if (ofpbuf_size(payload) < sizeof *prop) {
+        return OFPERR_OFPBPC_BAD_LEN;
+    }
+
+    experimenter = ntohl(prop->experimenter);
+    exp_type = ntohl(prop->exp_type);
+
+    switch (experimenter) {
+    case NMX_VENDOR_ID:
+        error = parse_group_prop_nmx(payload, exp_type, group_type,
+                                     group_cmd, gp);
+        break;
+
+    default:
+        log_property(false, "unknown group property experimenter %"PRIu16,
+                     experimenter);
+        error = OFPERR_OFPBPC_BAD_EXPERIMENTER;
+        break;
+    }
+
+    return error;
+}
+
+static enum ofperr
+parse_ofp15_group_properties(struct ofpbuf *msg,
+                             enum ofp11_group_type group_type,
+                             enum ofp15_group_mod_command group_cmd,
+                             struct ofputil_group_props *gp,
+                             size_t properties_len)
+{
+    struct ofpbuf properties;
+
+    ofpbuf_use_const(&properties, ofpbuf_pull(msg, properties_len),
+                     properties_len);
+
+    while (ofpbuf_size(&properties) > 0) {
+        struct ofpbuf payload;
+        enum ofperr error;
+        uint16_t type;
+
+        error = ofputil_pull_property(&properties, &payload, &type);
+        if (error) {
+            return error;
+        }
+
+        switch (type) {
+        case OFPGPT15_EXPERIMENTER:
+            error = parse_ofp15_group_prop_exp(&payload, group_type,
+                                               group_cmd, gp);
+            break;
+
+        default:
+            log_property(false, "unknown group property %"PRIu16, type);
+            error = OFPERR_OFPBPC_BAD_TYPE;
+            break;
+        }
+
+        if (error) {
+            return error;
+        }
+    }
+
+    return 0;
+}
+
 static int
 ofputil_decode_ofp11_group_desc_reply(struct ofputil_group_desc *gd,
                                       struct ofpbuf *msg,
@@ -7759,6 +7960,7 @@ ofputil_decode_ofp15_group_desc_reply(struct ofputil_group_desc *gd,
 {
     struct ofp15_group_desc_stats *ogds;
     uint16_t length, bucket_list_len;
+    int error;
 
     if (!msg->frame) {
         ofpraw_pull_assert(msg);
@@ -7790,9 +7992,22 @@ ofputil_decode_ofp15_group_desc_reply(struct ofputil_group_desc *gd,
                      "bucket list length %u", bucket_list_len);
         return OFPERR_OFPBRC_BAD_LEN;
     }
+    error = ofputil_pull_ofp15_buckets(msg, bucket_list_len, version,
+                                       &gd->buckets);
+    if (error) {
+        return error;
+    }
 
-    return ofputil_pull_ofp15_buckets(msg, bucket_list_len, version,
-                                      &gd->buckets);
+    /* By definition group desc messages don't have a group mod command.
+     * However, parse_group_prop_nmx_selection_method() checks to make sure
+     * that the command is OFPGC15_ADD or OFPGC15_DELETE to guard
+     * against group mod messages with other commands supplying
+     * a NMX selection method group experimenter property.
+     * Such properties are valid for group desc replies so
+     * claim that the group mod command is OFPGC15_ADD to
+     * satisfy the check in parse_group_prop_nmx_selection_method() */
+    return parse_ofp15_group_properties(msg, gd->type, OFPGC15_ADD, &gd->props,
+                                        ofpbuf_size(msg));
 }
 
 /* Converts a group description reply in 'msg' into an abstract
@@ -7809,6 +8024,8 @@ int
 ofputil_decode_group_desc_reply(struct ofputil_group_desc *gd,
                                 struct ofpbuf *msg, enum ofp_version version)
 {
+    ofputil_init_group_properties(&gd->props);
+
     switch (version)
     {
     case OFP11_VERSION:
@@ -7826,6 +8043,13 @@ ofputil_decode_group_desc_reply(struct ofputil_group_desc *gd,
     }
 }
 
+void
+ofputil_uninit_group_mod(struct ofputil_group_mod *gm)
+{
+    ofputil_bucket_list_destroy(&gm->buckets);
+    field_array_delete(&gm->props.fields);
+}
+
 static struct ofpbuf *
 ofputil_encode_ofp11_group_mod(enum ofp_version ofp_version,
                                const struct ofputil_group_mod *gm)
@@ -8048,14 +8272,14 @@ ofputil_pull_ofp15_group_mod(struct ofpbuf *msg, enum ofp_version ofp_version,
     }
 
     bucket_list_len = ntohs(ogm->bucket_array_len);
-    if (bucket_list_len < ofpbuf_size(msg)) {
-        VLOG_WARN_RL(&bad_ofmsg_rl, "group has %u trailing bytes",
-                     ofpbuf_size(msg) - bucket_list_len);
-        return OFPERR_OFPGMFC_BAD_BUCKET;
+    error = ofputil_pull_ofp15_buckets(msg, bucket_list_len, ofp_version,
+                                       &gm->buckets);
+    if (error) {
+        return error;
     }
 
-    return ofputil_pull_ofp15_buckets(msg, bucket_list_len, ofp_version,
-                                      &gm->buckets);
+    return parse_ofp15_group_properties(msg, gm->type, gm->command, &gm->props,
+                                        ofpbuf_size(msg));
 }
 
 /* Converts OpenFlow group mod message 'oh' into an abstract group mod in
@@ -8072,6 +8296,8 @@ ofputil_decode_group_mod(const struct ofp_header *oh,
     ofpbuf_use_const(&msg, oh, ntohs(oh->length));
     ofpraw_pull_assert(&msg);
 
+    ofputil_init_group_properties(&gm->props);
+
     switch (ofp_version)
     {
     case OFP11_VERSION:
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index df4d044..31b250b 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -27,6 +27,7 @@
 #include "match.h"
 #include "meta-flow.h"
 #include "netdev.h"
+#include "openflow/netronome-ext.h"
 #include "openflow/nicira-ext.h"
 #include "openvswitch/types.h"
 #include "type-props.h"
@@ -217,6 +218,8 @@ void ofputil_match_to_ofp10_match(const struct match *, struct ofp10_match *);
 /* Work with ofp11_match. */
 enum ofperr ofputil_pull_ofp11_match(struct ofpbuf *, struct match *,
                                      uint16_t *padded_match_len);
+enum ofperr ofputil_pull_ofp11_mask(struct ofpbuf *, struct match *,
+                                    struct mf_bitmap *bm);
 enum ofperr ofputil_match_from_ofp11_match(const struct ofp11_match *,
                                            struct match *);
 int ofputil_put_ofp11_match(struct ofpbuf *, const struct match *,
@@ -994,6 +997,14 @@ struct ofputil_bucket {
 };
 
 /* Protocol-independent group_mod. */
+struct ofputil_group_props {
+    /* NMX selection method */
+    char selection_method[NMX_MAX_SELECTION_METHOD_LEN];
+    uint64_t selection_method_param;
+    struct ovs_list fields;     /* A list of struct field_array */
+};
+
+/* Protocol-independent group_mod. */
 struct ofputil_group_mod {
     uint16_t command;             /* One of OFPGC15_*. */
     uint8_t type;                 /* One of OFPGT11_*. */
@@ -1003,6 +1014,7 @@ struct ofputil_group_mod {
                                    * OFPGC15_REMOVE_BUCKET commands
                                    * execution.*/
     struct ovs_list buckets;      /* Contains "struct ofputil_bucket"s. */
+    struct ofputil_group_props props; /* Group properties. */
 };
 
 /* Group stats reply, independent of protocol. */
@@ -1032,6 +1044,7 @@ struct ofputil_group_desc {
     uint8_t type;               /* One of OFPGT_*. */
     uint32_t group_id;          /* Group identifier. */
     struct ovs_list buckets;    /* Contains "struct ofputil_bucket"s. */
+    struct ofputil_group_props props; /* Group properties. */
 };
 
 void ofputil_bucket_list_destroy(struct ovs_list *buckets);
@@ -1062,6 +1075,7 @@ struct ofpbuf *ofputil_encode_group_features_reply(
     const struct ofputil_group_features *, const struct ofp_header *request);
 void ofputil_decode_group_features_reply(const struct ofp_header *,
                                          struct ofputil_group_features *);
+void ofputil_uninit_group_mod(struct ofputil_group_mod *gm);
 struct ofpbuf *ofputil_encode_group_mod(enum ofp_version ofp_version,
                                         const struct ofputil_group_mod *gm);
 
@@ -1071,6 +1085,7 @@ enum ofperr ofputil_decode_group_mod(const struct ofp_header *,
 int ofputil_decode_group_stats_reply(struct ofpbuf *,
                                      struct ofputil_group_stats *);
 
+void ofputil_uninit_group_desc(struct ofputil_group_desc *gd);
 uint32_t ofputil_decode_group_desc_request(const struct ofp_header *);
 struct ofpbuf *ofputil_encode_group_desc_request(enum ofp_version,
                                                  uint32_t group_id);
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index d114390..edf448a 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -499,6 +499,11 @@ struct ofgroup {
 
     struct ovs_list buckets;        /* Contains "struct ofputil_bucket"s. */
     const uint32_t n_buckets;
+
+    /* NMX selection method */
+    const char selection_method[NMX_MAX_SELECTION_METHOD_LEN];
+    const uint64_t selection_method_param;
+    const struct ovs_list fields;   /* List of other elements in the array */
 };
 
 bool ofproto_group_lookup(const struct ofproto *ofproto, uint32_t group_id,
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index b3909ad..2f4d62f 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -2673,6 +2673,7 @@ ofproto_group_unref(struct ofgroup *group)
     if (group && ovs_refcount_unref(&group->ref_count) == 1) {
         group->ofproto->ofproto_class->group_destruct(group);
         ofputil_bucket_list_destroy(&group->buckets);
+        field_array_delete(CONST_CAST(struct ovs_list *, &group->fields));
         group->ofproto->ofproto_class->group_dealloc(group);
     }
 }
@@ -5850,6 +5851,14 @@ init_group(struct ofproto *ofproto, struct ofputil_group_mod *gm,
     *CONST_CAST(uint32_t *, &(*ofgroup)->n_buckets) =
         list_size(&(*ofgroup)->buckets);
 
+    memcpy(CONST_CAST(char *, (*ofgroup)->selection_method),
+           gm->props.selection_method, NMX_MAX_SELECTION_METHOD_LEN);
+    *CONST_CAST(uint64_t *, &(*ofgroup)->selection_method_param) =
+        gm->props.selection_method_param;
+    list_move(CONST_CAST(struct ovs_list *, &(*ofgroup)->fields),
+              &gm->props.fields);
+    list_init(&gm->props.fields);
+
     /* Construct called BEFORE any locks are held. */
     error = ofproto->ofproto_class->group_construct(*ofgroup);
     if (error) {
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index b5ace5a..4c553b2 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -2127,7 +2127,7 @@ ofctl_group_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
     }
     ofctl_group_mod__(argv[1], gms, n_gms, usable_protocols);
     for (i = 0; i < n_gms; i++) {
-        ofputil_bucket_list_destroy(&gms[i].buckets);
+        ofputil_uninit_group_mod(gms + i);
     }
     free(gms);
 }
@@ -2148,7 +2148,7 @@ ofctl_group_mod(int argc, char *argv[], uint16_t command)
             ovs_fatal(0, "%s", error);
         }
         ofctl_group_mod__(argv[1], &gm, 1, usable_protocols);
-        ofputil_bucket_list_destroy(&gm.buckets);
+        ofputil_uninit_group_mod(&gm);
     }
 }
 
-- 
2.1.4




More information about the dev mailing list