return match_len;
}
+/* Appends to 'b' the nx_match format that expresses the tlv corresponding
+ * to 'id'. If mask is not all-ones then it is also formated as the value
+ * of the tlv. */
+static void
+nx_format_mask_tlv(struct ds *ds, enum mf_field_id id,
+ const union mf_value *mask)
+{
+ const struct mf_field *mf = mf_from_id(id);
+
+ ds_put_format(ds, "%s", mf->name);
+
+ if (!is_all_ones(mask, mf->n_bytes)) {
+ ds_put_char(ds, '=');
+ mf_format(mf, mask, NULL, ds);
+ }
+
+ ds_put_char(ds, ',');
+}
+
+/* Appends a string representation of 'fa_' to 'ds'.
+ * The TLVS value of 'fa_' is treated as a mask and
+ * only the name of fields is formated if it is all ones. */
+void
+oxm_format_field_array(struct ds *ds, const struct field_array *fa)
+{
+ size_t start_len = ds->length;
+ int i;
+
+ for (i = 0; i < MFF_N_IDS; i++) {
+ if (bitmap_is_set(fa->used.bm, i)) {
+ nx_format_mask_tlv(ds, i, &fa->value[i]);
+ }
+ }
+
+ if (ds->length > start_len) {
+ ds_chomp(ds, ',');
+ }
+}
+
+/* Appends to 'b' a series of OXM TLVs corresponding to the series
+ * of enum mf_field_id and value tuples in 'fa_'.
+ *
+ * OXM differs slightly among versions of OpenFlow. Specify the OpenFlow
+ * version in use as 'version'.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns the number of bytes appended to 'b'. May return zero. */
+int
+oxm_put_field_array(struct ofpbuf *b, const struct field_array *fa,
+ enum ofp_version version)
+{
+ size_t start_len = b->size;
+ int i;
+
+ /* Field arrays are only used with the group selection method
+ * property and group properties are only available in OpenFlow * 1.5+.
+ * So the following assertion should never fail.
+ *
+ * If support for older OpenFlow versions is desired then some care
+ * will need to be taken of different TLVs that handle the same
+ * flow fields. In particular:
+ * - VLAN_TCI, VLAN_VID and MFF_VLAN_PCP
+ * - IP_DSCP_MASK and DSCP_SHIFTED
+ * - REGS and XREGS
+ */
+ ovs_assert(version >= OFP15_VERSION);
+
+ for (i = 0; i < MFF_N_IDS; i++) {
+ if (bitmap_is_set(fa->used.bm, i)) {
+ nxm_put_unmasked(b, i, version, &fa->value[i],
+ mf_from_id(i)->n_bytes);
+ }
+ }
+
+ return b->size - start_len;
+}
+
static void
nx_put_header__(struct ofpbuf *b, uint64_t header, bool masked)
{
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);
+void oxm_format_field_array(struct ds *, const struct field_array *);
+int oxm_put_field_array(struct ofpbuf *, const struct field_array *,
+ enum ofp_version version);
/* Decoding and encoding OXM/NXM headers (just a field ID) or entries (a field
* ID followed by a value and possibly a mask). */
static void
ofp_print_group(struct ds *s, uint32_t group_id, uint8_t type,
- struct ovs_list *p_buckets, enum ofp_version ofp_version,
- bool suppress_type)
+ struct ovs_list *p_buckets, struct ofputil_group_props *props,
+ enum ofp_version ofp_version, bool suppress_type)
{
struct ofputil_bucket *bucket;
ds_put_format(s, ",type=%s", type_str[type > 4 ? 4 : type]);
}
+ if (props->selection_method[0]) {
+ size_t mark, start;
+
+ ds_put_format(s, ",selection_method=%s,", props->selection_method);
+ if (props->selection_method_param) {
+ ds_put_format(s, "selection_method_param=%"PRIu64",",
+ props->selection_method_param);
+ }
+
+ /* Allow rewinding to immediately before the trailing ',' */
+ mark = s->length - 1;
+
+ ds_put_cstr(s, "fields=");
+ start = s->length;
+ oxm_format_field_array(s, &props->fields);
+ if (s->length == start) {
+ ds_truncate(s, mark);
+ }
+ }
+
if (!p_buckets) {
return;
}
ds_put_char(s, '\n');
ds_put_char(s, ' ');
- ofp_print_group(s, gd.group_id, gd.type, &gd.buckets, oh->version,
- false);
+ ofp_print_group(s, gd.group_id, gd.type, &gd.buckets, &gd.props,
+ oh->version, false);
ofputil_bucket_list_destroy(&gd.buckets);
}
}
gm.command_bucket_id, oh->version);
}
- ofp_print_group(s, gm.group_id, gm.type, &gm.buckets, oh->version,
- bucket_command);
+ ofp_print_group(s, gm.group_id, gm.type, &gm.buckets, &gm.props,
+ oh->version, bucket_command);
ofputil_bucket_list_destroy(&gm.buckets);
}
ob->bucket_id = htonl(bucket_id);
}
+static void
+ofputil_put_group_prop_ntr_selection_method(enum ofp_version ofp_version,
+ const struct ofputil_group_props *gp,
+ struct ofpbuf *openflow)
+{
+ struct ntr_group_prop_selection_method *prop;
+ size_t start;
+
+ start = openflow->size;
+ ofpbuf_put_zeros(openflow, sizeof *prop);
+ oxm_put_field_array(openflow, &gp->fields, ofp_version);
+ prop = ofpbuf_at_assert(openflow, start, sizeof *prop);
+ prop->type = htons(OFPGPT15_EXPERIMENTER);
+ prop->experimenter = htonl(NTR_VENDOR_ID);
+ prop->exp_type = htonl(NTRT_SELECTION_METHOD);
+ strcpy(prop->selection_method, gp->selection_method);
+ prop->selection_method_param = htonll(gp->selection_method_param);
+ end_property(openflow, start);
+}
+
static void
ofputil_append_ofp11_group_desc_reply(const struct ofputil_group_desc *gds,
const struct ovs_list *buckets,
ogds->group_id = htonl(gds->group_id);
ogds->bucket_list_len = htons(reply->size - start_buckets);
+ /* Add group properties */
+ if (gds->props.selection_method[0]) {
+ ofputil_put_group_prop_ntr_selection_method(version, &gds->props,
+ reply);
+ }
+
ofpmp_postappend(replies, start_ogds);
}
ogm->command_bucket_id = htonl(gm->command_bucket_id);
ogm->bucket_array_len = htons(b->size - start_ogm - sizeof *ogm);
+ /* Add group properties */
+ if (gm->props.selection_method[0]) {
+ ofputil_put_group_prop_ntr_selection_method(ofp_version, &gm->props, b);
+ }
+
id_pool_destroy(bucket_ids);
return b;
}
gds.group_id = group->group_id;
gds.type = group->type;
+ gds.props = group->props;
+
ofputil_append_group_desc_reply(&gds, &group->buckets, replies);
}