ovs_be32 duration_sec; /* Time meter has been alive in seconds. */
ovs_be32 duration_nsec; /* Time meter has been alive in nanoseconds
beyond duration_sec. */
- /* struct ofp13_meter_band_stats band_stats[0]; The band_stats length is
+ struct ofp13_meter_band_stats band_stats[0]; /* The band_stats length is
inferred from the length field. */
};
OFP_ASSERT(sizeof(struct ofp13_meter_stats) == 40);
OFPRR_DELETE, /* Evicted by a DELETE flow mod. */
OFPRR_GROUP_DELETE, /* Group was removed. */
OFPRR_EVICTION, /* Switch eviction to free resources. */
+ OFPRR_METER_DELETE, /* Meter was removed. */
};
/* What changed about the physical port */
goto exit;
}
+ if (insts[OVSINST_OFPIT13_METER]) {
+ const struct ofp13_instruction_meter *oim;
+ struct ofpact_meter *om;
+
+ oim = (const struct ofp13_instruction_meter *)
+ insts[OVSINST_OFPIT13_METER];
+
+ om = ofpact_put_METER(ofpacts);
+ om->meter_id = ntohl(oim->meter_id);
+ }
if (insts[OVSINST_OFPIT11_APPLY_ACTIONS]) {
const union ofp_action *actions;
size_t n_actions;
case OFPACT_CLEAR_ACTIONS:
case OFPACT_WRITE_METADATA:
+ case OFPACT_METER:
case OFPACT_GOTO_TABLE:
return 0;
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
enum ovs_instruction_type next;
- if (a->type == OFPACT_CLEAR_ACTIONS) {
+ if (a->type == OFPACT_METER) {
+ next = OVSINST_OFPIT13_METER;
+ } else if (a->type == OFPACT_CLEAR_ACTIONS) {
next = OVSINST_OFPIT11_CLEAR_ACTIONS;
} else if (a->type == OFPACT_WRITE_METADATA) {
next = OVSINST_OFPIT11_WRITE_METADATA;
case OFPACT_SET_L4_DST_PORT:
case OFPACT_CLEAR_ACTIONS:
case OFPACT_GOTO_TABLE:
+ case OFPACT_METER:
NOT_REACHED();
}
}
case OFPACT_PUSH_VLAN:
case OFPACT_CLEAR_ACTIONS:
case OFPACT_GOTO_TABLE:
+ case OFPACT_METER:
/* XXX */
break;
case OFPACT_CLEAR_ACTIONS:
case OFPACT_GOTO_TABLE:
+ case OFPACT_METER:
NOT_REACHED();
case OFPACT_CONTROLLER:
oiwm = instruction_put_OFPIT11_WRITE_METADATA(openflow);
oiwm->metadata = om->metadata;
oiwm->metadata_mask = om->mask;
+ } else if (a->type == OFPACT_METER) {
+ const struct ofpact_meter *om;
+ struct ofp13_instruction_meter *oim;
+
+ om = ofpact_get_METER(a);
+ oim = instruction_put_OFPIT13_METER(openflow);
+ oim->meter_id = htonl(om->meter_id);
} else if (!ofpact_is_instruction(a)) {
/* Apply-actions */
const size_t ofs = openflow->size;
case OFPACT_SAMPLE:
case OFPACT_CLEAR_ACTIONS:
case OFPACT_GOTO_TABLE:
+ case OFPACT_METER:
default:
return false;
}
OVSINST_OFPIT11_GOTO_TABLE),
ofpact_get_GOTO_TABLE(a)->table_id);
break;
+
+ case OFPACT_METER:
+ ds_put_format(s, "%s:%"PRIu32,
+ ofpact_instruction_name_from_type(OVSINST_OFPIT13_METER),
+ ofpact_get_METER(a)->meter_id);
+ break;
}
}
DEFINE_OFPACT(SAMPLE, ofpact_sample, ofpact) \
\
/* Instructions */ \
+ DEFINE_OFPACT(METER, ofpact_meter, ofpact) \
/* XXX Write-Actions */ \
- DEFINE_OFPACT(WRITE_METADATA, ofpact_metadata, ofpact) \
DEFINE_OFPACT(CLEAR_ACTIONS, ofpact_null, ofpact) \
+ DEFINE_OFPACT(WRITE_METADATA, ofpact_metadata, ofpact) \
DEFINE_OFPACT(GOTO_TABLE, ofpact_goto_table, ofpact)
/* enum ofpact_type, with a member OFPACT_<ENUM> for each action. */
ovs_be64 mask;
};
+/* OFPACT_METER.
+ *
+ * Used for OFPIT13_METER. */
+struct ofpact_meter {
+ struct ofpact ofpact;
+ uint32_t meter_id;
+};
+
/* OFPACT_RESUBMIT.
*
* Used for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE. */
* It is enforced on parser from text string.
*/
#define OVS_INSTRUCTIONS \
+ DEFINE_INST(OFPIT13_METER, \
+ ofp13_instruction_meter, false, \
+ "meter") \
+ \
DEFINE_INST(OFPIT11_APPLY_ACTIONS, \
ofp11_instruction_actions, true, \
"apply_actions") \
{
/* XXX Write-Actions */
return a->type == OFPACT_CLEAR_ACTIONS
+ || a->type == OFPACT_METER
|| a->type == OFPACT_WRITE_METADATA
|| a->type == OFPACT_GOTO_TABLE;
}
/* NXT 1.0+ (19): struct nx_async_config. */
OFPRAW_NXT_SET_ASYNC_CONFIG,
- /* OFPT 1.3+ (29): struct ofp13_meter_mod. */
+ /* OFPT 1.3+ (29): struct ofp13_meter_mod, uint8_t[8][]. */
OFPRAW_OFPT13_METER_MOD,
/* Standard statistics. */
/* OFPST 1.3+ (9): struct ofp13_meter_multipart_request. */
OFPRAW_OFPST13_METER_REQUEST,
- /* OFPST 1.3+ (9): struct ofp13_meter_stats[]. */
+ /* OFPST 1.3+ (9): uint8_t[8][]. */
OFPRAW_OFPST13_METER_REPLY,
/* OFPST 1.3+ (10): struct ofp13_meter_multipart_request. */
OFPRAW_OFPST13_METER_CONFIG_REQUEST,
- /* OFPST 1.3+ (10): struct ofp13_meter_config[]. */
+ /* OFPST 1.3+ (10): uint8_t[8][]. */
OFPRAW_OFPST13_METER_CONFIG_REPLY,
/* OFPST 1.3+ (11): void. */
NO_RETURN;
static uint8_t
-str_to_table_id(const char *str)
+str_to_u8(const char *str, const char *name)
{
- int table_id;
+ int value;
- if (!str_to_int(str, 10, &table_id) || table_id < 0 || table_id > 255) {
- ovs_fatal(0, "invalid table \"%s\"", str);
+ if (!str_to_int(str, 10, &value) || value < 0 || value > 255) {
+ ovs_fatal(0, "invalid %s \"%s\"", name, str);
}
- return table_id;
+ return value;
}
static uint16_t
ofpact_put_CLEAR_ACTIONS(ofpacts);
break;
+ case OVSINST_OFPIT13_METER:
+ ofpact_put_METER(ofpacts)->meter_id = str_to_u32(arg);
+ break;
+
case OVSINST_OFPIT11_WRITE_METADATA:
parse_metadata(ofpacts, arg);
break;
if (!table_s || !table_s[0]) {
ovs_fatal(0, "instruction goto-table needs table id");
}
- ogt->table_id = str_to_table_id(table_s);
+ ogt->table_id = str_to_u8(table_s, "table");
break;
}
}
}
if (!strcmp(name, "table")) {
- fm->table_id = str_to_table_id(value);
+ fm->table_id = str_to_u8(value, name);
} else if (!strcmp(name, "out_port")) {
if (!ofputil_port_from_string(name, &fm->out_port)) {
ofp_fatal(str_, verbose, "%s is not a valid OpenFlow port",
free(string);
}
+/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
+ * page) into 'mm' for sending the specified meter_mod 'command' to a switch.
+ */
+void
+parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
+ int command, bool verbose)
+{
+ enum {
+ F_METER = 1 << 0,
+ F_FLAGS = 1 << 1,
+ F_BANDS = 1 << 2,
+ } fields;
+ char *string = xstrdup(str_);
+ char *save_ptr = NULL;
+ char *band_str = NULL;
+ char *name;
+
+ switch (command) {
+ case -1:
+ fields = F_METER;
+ break;
+
+ case OFPMC13_ADD:
+ fields = F_METER | F_FLAGS | F_BANDS;
+ break;
+
+ case OFPMC13_DELETE:
+ fields = F_METER;
+ break;
+
+ case OFPMC13_MODIFY:
+ fields = F_METER | F_FLAGS | F_BANDS;
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+
+ mm->command = command;
+ mm->meter.meter_id = 0;
+ mm->meter.flags = 0;
+ if (fields & F_BANDS) {
+ band_str = strstr(string, "band");
+ if (!band_str) {
+ ofp_fatal(str_, verbose, "must specify bands");
+ }
+ *band_str = '\0';
+
+ band_str = strchr(band_str + 1, '=');
+ if (!band_str) {
+ ofp_fatal(str_, verbose, "must specify bands");
+ }
+
+ band_str++;
+ }
+ for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
+ name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+
+ if (fields & F_FLAGS && !strcmp(name, "kbps")) {
+ mm->meter.flags |= OFPMF13_KBPS;
+ } else if (fields & F_FLAGS && !strcmp(name, "pktps")) {
+ mm->meter.flags |= OFPMF13_PKTPS;
+ } else if (fields & F_FLAGS && !strcmp(name, "burst")) {
+ mm->meter.flags |= OFPMF13_BURST;
+ } else if (fields & F_FLAGS && !strcmp(name, "stats")) {
+ mm->meter.flags |= OFPMF13_STATS;
+ } else {
+ char *value;
+
+ value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+ if (!value) {
+ ofp_fatal(str_, verbose, "field %s missing value", name);
+ }
+
+ if (!strcmp(name, "meter")) {
+ if (!strcmp(value, "all")) {
+ mm->meter.meter_id = OFPM13_ALL;
+ } else if (!strcmp(value, "controller")) {
+ mm->meter.meter_id = OFPM13_CONTROLLER;
+ } else if (!strcmp(value, "slowpath")) {
+ mm->meter.meter_id = OFPM13_SLOWPATH;
+ } else {
+ mm->meter.meter_id = str_to_u32(value);
+ if (mm->meter.meter_id > OFPM13_MAX) {
+ ofp_fatal(str_, verbose, "invalid value for %s", name);
+ }
+ }
+ } else {
+ ofp_fatal(str_, verbose, "unknown keyword %s", name);
+ }
+ }
+ }
+ if (fields & F_METER && !mm->meter.meter_id) {
+ ofp_fatal(str_, verbose, "must specify 'meter'");
+ }
+ if (fields & F_FLAGS && !mm->meter.flags) {
+ ofp_fatal(str_, verbose,
+ "meter must specify either 'kbps' or 'pktps'");
+ }
+
+ if (fields & F_BANDS) {
+ struct ofpbuf bands;
+ uint16_t n_bands = 0;
+ struct ofputil_meter_band *band = NULL;
+ int i;
+
+ ofpbuf_init(&bands, 64);
+
+ for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name;
+ name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+
+ char *value;
+
+ value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+ if (!value) {
+ ofp_fatal(str_, verbose, "field %s missing value", name);
+ }
+
+ if (!strcmp(name, "type")) {
+ /* Start a new band */
+ band = ofpbuf_put_zeros(&bands, sizeof *band);
+ n_bands++;
+
+ if (!strcmp(value, "drop")) {
+ band->type = OFPMBT13_DROP;
+ } else if (!strcmp(value, "dscp_remark")) {
+ band->type = OFPMBT13_DSCP_REMARK;
+ } else {
+ ofp_fatal(str_, verbose, "field %s unknown value %s", name,
+ value);
+ }
+ } else if (!band || !band->type) {
+ ofp_fatal(str_, verbose,
+ "band must start with the 'type' keyword");
+ } else if (!strcmp(name, "rate")) {
+ band->rate = str_to_u32(value);
+ } else if (!strcmp(name, "burst_size")) {
+ band->burst_size = str_to_u32(value);
+ } else if (!strcmp(name, "prec_level")) {
+ band->prec_level = str_to_u8(value, name);
+ } else {
+ ofp_fatal(str_, verbose, "unknown keyword %s", name);
+ }
+ }
+ /* validate bands */
+ if (!n_bands) {
+ ofp_fatal(str_, verbose, "meter must have bands");
+ }
+
+ mm->meter.n_bands = n_bands;
+ mm->meter.bands = ofpbuf_steal_data(&bands);
+
+ for (i = 0; i < n_bands; ++i) {
+ band = &mm->meter.bands[i];
+
+ if (!band->type) {
+ ofp_fatal(str_, verbose, "band must have 'type'");
+ }
+ if (band->type == OFPMBT13_DSCP_REMARK) {
+ if (!band->prec_level) {
+ ofp_fatal(str_, verbose, "'dscp_remark' band must have"
+ " 'prec_level'");
+ }
+ } else {
+ if (band->prec_level) {
+ ofp_fatal(str_, verbose, "Only 'dscp_remark' band may have"
+ " 'prec_level'");
+ }
+ }
+ if (!band->rate) {
+ ofp_fatal(str_, verbose, "band must have 'rate'");
+ }
+ if (mm->meter.flags & OFPMF13_BURST) {
+ if (!band->burst_size) {
+ ofp_fatal(str_, verbose, "band must have 'burst_size' "
+ "when 'burst' flag is set");
+ }
+ } else {
+ if (band->burst_size) {
+ ofp_fatal(str_, verbose, "band may have 'burst_size' only "
+ "when 'burst' flag is set");
+ }
+ }
+ }
+ } else {
+ mm->meter.n_bands = 0;
+ mm->meter.bands = NULL;
+ }
+
+ free(string);
+}
+
/* Convert 'str_' (as described in the documentation for the "monitor" command
* in the ovs-ofctl man page) into 'fmr'. */
void
}
if (!strcmp(name, "table")) {
- fmr->table_id = str_to_table_id(value);
+ fmr->table_id = str_to_u8(value, name);
} else if (!strcmp(name, "out_port")) {
fmr->out_port = u16_to_ofp(atoi(value));
} else if (mf_from_name(name)) {
struct ofputil_flow_mod;
struct ofputil_flow_monitor_request;
struct ofputil_flow_stats_request;
+struct ofputil_meter_mod;
void parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
bool verbose);
char *parse_ofp_exact_flow(struct flow *, const char *);
+void parse_ofp_meter_mod_str(struct ofputil_meter_mod *, const char *string,
+ int command, bool verbose);
+
void parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
const char *);
return "group_delete";
case OFPRR_EVICTION:
return "eviction";
+ case OFPRR_METER_DELETE:
+ return "meter_delete";
default:
snprintf(reasonbuf, bufsize, "%d", (int) reason);
return reasonbuf;
}
}
+static void
+ofp_print_meter_flags(struct ds *s, uint16_t flags)
+{
+ if (flags & OFPMF13_KBPS) {
+ ds_put_cstr(s, "kbps ");
+ }
+ if (flags & OFPMF13_PKTPS) {
+ ds_put_cstr(s, "pktps ");
+ }
+ if (flags & OFPMF13_BURST) {
+ ds_put_cstr(s, "burst ");
+ }
+ if (flags & OFPMF13_STATS) {
+ ds_put_cstr(s, "stats ");
+ }
+
+ flags &= ~(OFPMF13_KBPS | OFPMF13_PKTPS | OFPMF13_BURST | OFPMF13_STATS);
+ if (flags) {
+ ds_put_format(s, "flags:0x%"PRIx16" ", flags);
+ }
+}
+
+static void
+ofp_print_meter_band(struct ds *s, uint16_t flags,
+ const struct ofputil_meter_band *mb)
+{
+ ds_put_cstr(s, "\ntype=");
+ switch (mb->type) {
+ case OFPMBT13_DROP:
+ ds_put_cstr(s, "drop");
+ break;
+ case OFPMBT13_DSCP_REMARK:
+ ds_put_cstr(s, "dscp_remark");
+ break;
+ default:
+ ds_put_format(s, "%u", mb->type);
+ }
+
+ ds_put_format(s, " rate=%"PRIu32, mb->rate);
+
+ if (flags & OFPMF13_BURST) {
+ ds_put_format(s, " burst_size=%"PRIu32, mb->burst_size);
+ }
+ if (mb->type == OFPMBT13_DSCP_REMARK) {
+ ds_put_format(s, " prec_level=%"PRIu8, mb->prec_level);
+ }
+}
+
+static void
+ofp_print_meter_stats(struct ds *s, const struct ofputil_meter_stats *ms)
+{
+ uint16_t i;
+
+ ds_put_format(s, "meter:%"PRIu32" ", ms->meter_id);
+ ds_put_format(s, "flow_count:%"PRIu32" ", ms->flow_count);
+ ds_put_format(s, "packet_in_count:%"PRIu64" ", ms->packet_in_count);
+ ds_put_format(s, "byte_in_count:%"PRIu64" ", ms->byte_in_count);
+ ds_put_cstr(s, "duration:");
+ ofp_print_duration(s, ms->duration_sec, ms->duration_nsec);
+ ds_put_char(s, ' ');
+
+ ds_put_cstr(s, "bands:\n");
+ for (i = 0; i < ms->n_bands; ++i) {
+ ds_put_format(s, "%d: ", i);
+ ds_put_format(s, "packet_count:%"PRIu64" ", ms->bands[i].packet_count);
+ ds_put_format(s, "byte_count:%"PRIu64"\n", ms->bands[i].byte_count);
+ }
+}
+
+static void
+ofp_print_meter_config(struct ds *s, const struct ofputil_meter_config *mc)
+{
+ uint16_t i;
+
+ ds_put_format(s, "meter=%"PRIu32" ", mc->meter_id);
+
+ ofp_print_meter_flags(s, mc->flags);
+
+ ds_put_cstr(s, "bands=");
+ for (i = 0; i < mc->n_bands; ++i) {
+ ofp_print_meter_band(s, mc->flags, &mc->bands[i]);
+ }
+ ds_put_char(s, '\n');
+}
+
+static void
+ofp_print_meter_mod(struct ds *s, const struct ofp_header *oh)
+{
+ struct ofputil_meter_mod mm;
+ struct ofpbuf bands;
+ enum ofperr error;
+
+ ofpbuf_init(&bands, 64);
+ error = ofputil_decode_meter_mod(oh, &mm, &bands);
+ if (error) {
+ ofpbuf_uninit(&bands);
+ ofp_print_error(s, error);
+ return;
+ }
+
+ switch (mm.command) {
+ case OFPMC13_ADD:
+ ds_put_cstr(s, " ADD ");
+ break;
+ case OFPMC13_MODIFY:
+ ds_put_cstr(s, " MOD ");
+ break;
+ case OFPMC13_DELETE:
+ ds_put_cstr(s, " DEL ");
+ break;
+ default:
+ ds_put_format(s, " cmd:%d ", mm.command);
+ }
+
+ ofp_print_meter_config(s, &mm.meter);
+ ofpbuf_uninit(&bands);
+}
+
+static void
+ofp_print_meter_stats_request(struct ds *s, const struct ofp_header *oh)
+{
+ uint32_t meter_id;
+
+ ofputil_decode_meter_request(oh, &meter_id);
+
+ ds_put_format(s, " meter=%"PRIu32, meter_id);
+}
+
+static const char *
+ofputil_meter_capabilities_to_name(uint32_t bit)
+{
+ enum ofp13_meter_flags flag = bit;
+
+ switch (flag) {
+ case OFPMF13_KBPS: return "kbps";
+ case OFPMF13_PKTPS: return "pktps";
+ case OFPMF13_BURST: return "burst";
+ case OFPMF13_STATS: return "stats";
+ }
+
+ return NULL;
+}
+
+static const char *
+ofputil_meter_band_types_to_name(uint32_t bit)
+{
+ /*
+ * Note: Meter band types start from 1. We assume that the lowest bit
+ * in the band_types corresponds to DROP band type (1).
+ */
+ switch (bit) {
+ case 1 << (OFPMBT13_DROP - 1): return "drop";
+ case 1 << (OFPMBT13_DSCP_REMARK - 1): return "dscp_remark";
+ }
+
+ return NULL;
+}
+
+static void
+ofp_print_meter_features_reply(struct ds *s, const struct ofp_header *oh)
+{
+ struct ofputil_meter_features mf;
+
+ ofputil_decode_meter_features(oh, &mf);
+
+ ds_put_format(s, "\nmax_meter:%"PRIu32, mf.max_meters);
+ ds_put_format(s, " max_bands:%"PRIu8, mf.max_bands);
+ ds_put_format(s, " max_color:%"PRIu8"\n", mf.max_color);
+
+ ds_put_cstr(s, "band_types: ");
+ ofp_print_bit_names(s, mf.band_types,
+ ofputil_meter_band_types_to_name, ' ');
+ ds_put_char(s, '\n');
+
+ ds_put_cstr(s, "capabilities: ");
+ ofp_print_bit_names(s, mf.capabilities,
+ ofputil_meter_capabilities_to_name, ' ');
+ ds_put_char(s, '\n');
+}
+
+static void
+ofp_print_meter_config_reply(struct ds *s, const struct ofp_header *oh)
+{
+ struct ofpbuf bands;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpbuf_init(&bands, 64);
+ for (;;) {
+ struct ofputil_meter_config mc;
+ int retval;
+
+ retval = ofputil_decode_meter_config(&b, &mc, &bands);
+ if (retval) {
+ if (retval != EOF) {
+ ofp_print_error(s, retval);
+ }
+ break;
+ }
+ ds_put_char(s, '\n');
+ ofp_print_meter_config(s, &mc);
+ }
+ ofpbuf_uninit(&bands);
+}
+
+static void
+ofp_print_meter_stats_reply(struct ds *s, const struct ofp_header *oh)
+{
+ struct ofpbuf bands;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpbuf_init(&bands, 64);
+ for (;;) {
+ struct ofputil_meter_stats ms;
+ int retval;
+
+ retval = ofputil_decode_meter_stats(&b, &ms, &bands);
+ if (retval) {
+ if (retval != EOF) {
+ ofp_print_error(s, retval);
+ }
+ break;
+ }
+ ds_put_char(s, '\n');
+ ofp_print_meter_stats(s, &ms);
+ }
+ ofpbuf_uninit(&bands);
+}
+
static void
ofp_print_error(struct ds *string, enum ofperr error)
{
case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
case OFPTYPE_GET_ASYNC_REQUEST:
case OFPTYPE_GET_ASYNC_REPLY:
- case OFPTYPE_METER_MOD:
case OFPTYPE_GROUP_REQUEST:
case OFPTYPE_GROUP_REPLY:
case OFPTYPE_GROUP_DESC_REQUEST:
case OFPTYPE_GROUP_DESC_REPLY:
case OFPTYPE_GROUP_FEATURES_REQUEST:
case OFPTYPE_GROUP_FEATURES_REPLY:
- case OFPTYPE_METER_REQUEST:
- case OFPTYPE_METER_REPLY:
- case OFPTYPE_METER_CONFIG_REQUEST:
- case OFPTYPE_METER_CONFIG_REPLY:
- case OFPTYPE_METER_FEATURES_REQUEST:
- case OFPTYPE_METER_FEATURES_REPLY:
case OFPTYPE_TABLE_FEATURES_REQUEST:
case OFPTYPE_TABLE_FEATURES_REPLY:
ofp_print_not_implemented(string);
ofp_print_port_mod(string, oh);
break;
+ case OFPTYPE_METER_MOD:
+ ofp_print_meter_mod(string, oh);
+ break;
+
case OFPTYPE_BARRIER_REQUEST:
case OFPTYPE_BARRIER_REPLY:
break;
ofp_print_role_message(string, oh);
break;
+ case OFPTYPE_METER_REQUEST:
+ case OFPTYPE_METER_CONFIG_REQUEST:
+ ofp_print_stats_request(string, oh);
+ ofp_print_meter_stats_request(string, oh);
+ break;
+
+ case OFPTYPE_METER_REPLY:
+ ofp_print_stats_reply(string, oh);
+ ofp_print_meter_stats_reply(string, oh);
+ break;
+
+ case OFPTYPE_METER_CONFIG_REPLY:
+ ofp_print_stats_reply(string, oh);
+ ofp_print_meter_config_reply(string, oh);
+ break;
+
+ case OFPTYPE_METER_FEATURES_REPLY:
+ ofp_print_stats_reply(string, oh);
+ ofp_print_meter_features_reply(string, oh);
+ break;
+
case OFPTYPE_DESC_STATS_REQUEST:
case OFPTYPE_PORT_DESC_STATS_REQUEST:
+ case OFPTYPE_METER_FEATURES_REQUEST:
ofp_print_stats_request(string, oh);
break;
return 0;
}
+static enum ofperr
+ofputil_pull_bands(struct ofpbuf *msg, size_t len, uint16_t *n_bands,
+ struct ofpbuf *bands)
+{
+ const struct ofp13_meter_band_header *ombh;
+ struct ofputil_meter_band *mb;
+ uint16_t n = 0;
+
+ ombh = ofpbuf_try_pull(msg, len);
+ if (!ombh) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ while (len >= sizeof (struct ofp13_meter_band_drop)) {
+ size_t ombh_len = ntohs(ombh->len);
+ /* All supported band types have the same length */
+ if (ombh_len != sizeof (struct ofp13_meter_band_drop)) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ mb = ofpbuf_put_uninit(bands, sizeof *mb);
+ mb->type = ntohs(ombh->type);
+ mb->rate = ntohl(ombh->rate);
+ mb->burst_size = ntohl(ombh->burst_size);
+ mb->prec_level = (mb->type == OFPMBT13_DSCP_REMARK) ?
+ ((struct ofp13_meter_band_dscp_remark *)ombh)->prec_level : 0;
+ n++;
+ len -= ombh_len;
+ ombh = (struct ofp13_meter_band_header *)(((char *)ombh) + ombh_len);
+ }
+ if (len) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ *n_bands = n;
+ return 0;
+}
+
+enum ofperr
+ofputil_decode_meter_mod(const struct ofp_header *oh,
+ struct ofputil_meter_mod *mm,
+ struct ofpbuf *bands)
+{
+ const struct ofp13_meter_mod *omm;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+ omm = ofpbuf_pull(&b, sizeof *omm);
+
+ /* Translate the message. */
+ mm->command = ntohs(omm->command);
+ mm->meter.meter_id = ntohl(omm->meter_id);
+
+ if (mm->command == OFPMC13_DELETE) {
+ mm->meter.flags = 0;
+ mm->meter.n_bands = 0;
+ mm->meter.bands = NULL;
+ } else {
+ enum ofperr error;
+
+ mm->meter.flags = ntohs(omm->flags);
+ mm->meter.bands = bands->data;
+
+ error = ofputil_pull_bands(&b, b.size, &mm->meter.n_bands,
+ bands);
+ if (error) {
+ return error;
+ }
+ }
+ return 0;
+}
+
+void
+ofputil_decode_meter_request(const struct ofp_header *oh, uint32_t *meter_id)
+{
+ const struct ofp13_meter_multipart_request *omr = ofpmsg_body(oh);
+ *meter_id = ntohl(omr->meter_id);
+}
+
+struct ofpbuf *
+ofputil_encode_meter_request(enum ofp_version ofp_version,
+ enum ofputil_meter_request_type type,
+ uint32_t meter_id)
+{
+ struct ofpbuf *msg;
+
+ enum ofpraw raw;
+
+ switch (type) {
+ case OFPUTIL_METER_CONFIG:
+ raw = OFPRAW_OFPST13_METER_CONFIG_REQUEST;
+ break;
+ case OFPUTIL_METER_STATS:
+ raw = OFPRAW_OFPST13_METER_REQUEST;
+ break;
+ default:
+ case OFPUTIL_METER_FEATURES:
+ raw = OFPRAW_OFPST13_METER_FEATURES_REQUEST;
+ break;
+ }
+
+ msg = ofpraw_alloc(raw, ofp_version, 0);
+
+ if (type != OFPUTIL_METER_FEATURES) {
+ struct ofp13_meter_multipart_request *omr;
+ omr = ofpbuf_put_zeros(msg, sizeof *omr);
+ omr->meter_id = htonl(meter_id);
+ }
+ return msg;
+}
+
+static void
+ofputil_put_bands(uint16_t n_bands, const struct ofputil_meter_band *mb,
+ struct ofpbuf *msg)
+{
+ uint16_t n = 0;
+
+ for (n = 0; n < n_bands; ++n) {
+ /* Currently all band types have same size */
+ struct ofp13_meter_band_dscp_remark *ombh;
+ size_t ombh_len = sizeof *ombh;
+
+ ombh = ofpbuf_put_zeros(msg, ombh_len);
+
+ ombh->type = htons(mb->type);
+ ombh->len = htons(ombh_len);
+ ombh->rate = htonl(mb->rate);
+ ombh->burst_size = htonl(mb->burst_size);
+ ombh->prec_level = mb->prec_level;
+
+ mb++;
+ }
+}
+
+/* Encode a meter stat for 'mc' and append it to 'replies'. */
+void
+ofputil_append_meter_config(struct list *replies,
+ const struct ofputil_meter_config *mc)
+{
+ struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
+ size_t start_ofs = msg->size;
+ struct ofp13_meter_config *reply = ofpbuf_put_uninit(msg, sizeof *reply);
+ reply->flags = htons(mc->flags);
+ reply->meter_id = htonl(mc->meter_id);
+
+ ofputil_put_bands(mc->n_bands, mc->bands, msg);
+
+ reply->length = htons(msg->size - start_ofs);
+
+ ofpmp_postappend(replies, start_ofs);
+}
+
+/* Encode a meter stat for 'ms' and append it to 'replies'. */
+void
+ofputil_append_meter_stats(struct list *replies,
+ const struct ofputil_meter_stats *ms)
+{
+ struct ofp13_meter_stats *reply;
+ uint16_t n = 0;
+ uint16_t len;
+
+ len = sizeof *reply + ms->n_bands * sizeof(struct ofp13_meter_band_stats);
+ reply = ofpmp_append(replies, len);
+
+ reply->meter_id = htonl(ms->meter_id);
+ reply->len = htons(len);
+ memset(reply->pad, 0, sizeof reply->pad);
+ reply->flow_count = htonl(ms->flow_count);
+ reply->packet_in_count = htonll(ms->packet_in_count);
+ reply->byte_in_count = htonll(ms->byte_in_count);
+ reply->duration_sec = htonl(ms->duration_sec);
+ reply->duration_nsec = htonl(ms->duration_nsec);
+
+ for (n = 0; n < ms->n_bands; ++n) {
+ const struct ofputil_meter_band_stats *src = &ms->bands[n];
+ struct ofp13_meter_band_stats *dst = &reply->band_stats[n];
+
+ dst->packet_band_count = htonll(src->packet_count);
+ dst->byte_band_count = htonll(src->byte_count);
+ }
+}
+
+/* Converts an OFPMP_METER_CONFIG reply in 'msg' into an abstract
+ * ofputil_meter_config in 'mc', with mc->bands pointing to bands decoded into
+ * 'bands'. The caller must have initialized 'bands' and retains ownership of
+ * it across the call.
+ *
+ * Multiple OFPST13_METER_CONFIG replies can be packed into a single OpenFlow
+ * message. Calling this function multiple times for a single 'msg' iterates
+ * through the replies. 'bands' is cleared for each reply.
+ *
+ * Returns 0 if successful, EOF if no replies were left in this 'msg',
+ * otherwise a positive errno value. */
+int
+ofputil_decode_meter_config(struct ofpbuf *msg,
+ struct ofputil_meter_config *mc,
+ struct ofpbuf *bands)
+{
+ const struct ofp13_meter_config *omc;
+ enum ofperr err;
+
+ /* Pull OpenFlow headers for the first call. */
+ if (!msg->l2) {
+ ofpraw_pull_assert(msg);
+ }
+
+ if (!msg->size) {
+ return EOF;
+ }
+
+ omc = ofpbuf_try_pull(msg, sizeof *omc);
+ if (!omc) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPMP_METER_CONFIG reply has %zu "
+ "leftover bytes at end", msg->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ ofpbuf_clear(bands);
+ err = ofputil_pull_bands(msg, ntohs(omc->length) - sizeof *omc,
+ &mc->n_bands, bands);
+ if (err) {
+ return err;
+ }
+ mc->meter_id = ntohl(omc->meter_id);
+ mc->flags = ntohs(omc->flags);
+ mc->bands = bands->data;
+
+ return 0;
+}
+
+static enum ofperr
+ofputil_pull_band_stats(struct ofpbuf *msg, size_t len, uint16_t *n_bands,
+ struct ofpbuf *bands)
+{
+ const struct ofp13_meter_band_stats *ombs;
+ struct ofputil_meter_band_stats *mbs;
+ uint16_t n, i;
+
+ n = len / sizeof *ombs;
+ if (len != n * sizeof *ombs) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ ombs = ofpbuf_pull(msg, len);
+
+ mbs = ofpbuf_put_uninit(bands, len);
+
+ for (i = 0; i < n; ++i) {
+ mbs[i].packet_count = ntohll(ombs[i].packet_band_count);
+ mbs[i].byte_count = ntohll(ombs[i].byte_band_count);
+ }
+ *n_bands = n;
+ return 0;
+}
+
+/* Converts an OFPMP_METER reply in 'msg' into an abstract
+ * ofputil_meter_stats in 'ms', with ms->bands pointing to band stats
+ * decoded into 'bands'.
+ *
+ * Multiple OFPMP_METER replies can be packed into a single OpenFlow
+ * message. Calling this function multiple times for a single 'msg' iterates
+ * through the replies. 'bands' is cleared for each reply.
+ *
+ * Returns 0 if successful, EOF if no replies were left in this 'msg',
+ * otherwise a positive errno value. */
+int
+ofputil_decode_meter_stats(struct ofpbuf *msg,
+ struct ofputil_meter_stats *ms,
+ struct ofpbuf *bands)
+{
+ const struct ofp13_meter_stats *oms;
+ uint16_t len;
+ enum ofperr err;
+
+ /* Pull OpenFlow headers for the first call. */
+ if (!msg->l2) {
+ ofpraw_pull_assert(msg);
+ }
+
+ if (!msg->size) {
+ return EOF;
+ }
+
+ oms = ofpbuf_try_pull(msg, sizeof *oms);
+ if (!oms) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPMP_METER reply has %zu leftover "
+ "bytes at end", msg->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ len = ntohs(oms->len);
+ len -= sizeof *oms;
+
+ ofpbuf_clear(bands);
+ err = ofputil_pull_band_stats(msg, len, &ms->n_bands, bands);
+ if (err) {
+ return err;
+ }
+ ms->meter_id = ntohl(oms->meter_id);
+ ms->flow_count = ntohl(oms->flow_count);
+ ms->packet_in_count = ntohll(oms->packet_in_count);
+ ms->byte_in_count = ntohll(oms->byte_in_count);
+ ms->duration_sec = ntohl(oms->duration_sec);
+ ms->duration_nsec = ntohl(oms->duration_nsec);
+ ms->bands = bands->data;
+
+ return 0;
+}
+
+void
+ofputil_decode_meter_features(const struct ofp_header *oh,
+ struct ofputil_meter_features *mf)
+{
+ const struct ofp13_meter_features *omf = ofpmsg_body(oh);
+
+ mf->max_meters = ntohl(omf->max_meter);
+ mf->band_types = ntohl(omf->band_types);
+ mf->capabilities = ntohl(omf->capabilities);
+ mf->max_bands = omf->max_bands;
+ mf->max_color = omf->max_color;
+}
+
+struct ofpbuf *
+ofputil_encode_meter_features_reply(const struct ofputil_meter_features *mf,
+ const struct ofp_header *request)
+{
+ struct ofpbuf *reply;
+ struct ofp13_meter_features *omf;
+
+ reply = ofpraw_alloc_stats_reply(request, 0);
+ omf = ofpbuf_put_zeros(reply, sizeof *omf);
+
+ omf->max_meter = htonl(mf->max_meters);
+ omf->band_types = htonl(mf->band_types);
+ omf->capabilities = htonl(mf->capabilities);
+ omf->max_bands = mf->max_bands;
+ omf->max_color = mf->max_color;
+
+ return reply;
+}
+
+struct ofpbuf *
+ofputil_encode_meter_mod(enum ofp_version ofp_version,
+ const struct ofputil_meter_mod *mm)
+{
+ struct ofpbuf *msg;
+
+ struct ofp13_meter_mod *omm;
+
+ msg = ofpraw_alloc(OFPRAW_OFPT13_METER_MOD, ofp_version,
+ NXM_TYPICAL_LEN + mm->meter.n_bands * 16);
+ omm = ofpbuf_put_zeros(msg, sizeof *omm);
+ omm->command = htons(mm->command);
+ if (mm->command != OFPMC13_DELETE) {
+ omm->flags = htons(mm->meter.flags);
+ }
+ omm->meter_id = htonl(mm->meter.meter_id);
+
+ ofputil_put_bands(mm->meter.n_bands, mm->meter.bands, msg);
+
+ ofpmsg_update_length(msg);
+ return msg;
+}
+
static ovs_be16
ofputil_tid_command(const struct ofputil_flow_mod *fm,
enum ofputil_protocol protocol)
struct ofpbuf *ofputil_encode_port_mod(const struct ofputil_port_mod *,
enum ofputil_protocol);
+struct ofputil_meter_band {
+ uint16_t type;
+ uint8_t pad;
+ uint8_t prec_level; /* Non-zero if type == OFPMBT_DSCP_REMARK */
+ uint32_t rate;
+ uint32_t burst_size;
+};
+
+struct ofputil_meter_band_stats {
+ uint64_t packet_count;
+ uint64_t byte_count;
+};
+
+struct ofputil_meter_config {
+ uint32_t meter_id;
+ uint16_t flags;
+ uint16_t n_bands;
+ struct ofputil_meter_band *bands;
+};
+
+/* Abstract ofp_meter_mod. */
+struct ofputil_meter_mod {
+ uint16_t command;
+ struct ofputil_meter_config meter;
+};
+
+struct ofputil_meter_stats {
+ uint32_t meter_id;
+ uint32_t flow_count;
+ uint64_t packet_in_count;
+ uint64_t byte_in_count;
+ uint32_t duration_sec;
+ uint32_t duration_nsec;
+ uint16_t n_bands;
+ struct ofputil_meter_band_stats *bands; /* band stats */
+};
+
+struct ofputil_meter_features {
+ uint32_t max_meters; /* Maximum number of meters */
+ uint32_t band_types; /* Can support max 32 band types */
+ uint32_t capabilities; /* Supported flags */
+ uint8_t max_bands;
+ uint8_t max_color;
+};
+
+enum ofperr ofputil_decode_meter_mod(const struct ofp_header *,
+ struct ofputil_meter_mod *,
+ struct ofpbuf *bands);
+struct ofpbuf *ofputil_encode_meter_mod(enum ofp_version ofp_version,
+ const struct ofputil_meter_mod *);
+
+void ofputil_decode_meter_features(const struct ofp_header *,
+ struct ofputil_meter_features *);
+struct ofpbuf *ofputil_encode_meter_features_reply(const struct
+ ofputil_meter_features *,
+ const struct ofp_header *
+ request);
+void ofputil_decode_meter_request(const struct ofp_header *,
+ uint32_t *meter_id);
+
+void ofputil_append_meter_config(struct list *replies,
+ const struct ofputil_meter_config *omc);
+
+void ofputil_append_meter_stats(struct list *replies,
+ const struct ofputil_meter_stats *oms);
+
+enum ofputil_meter_request_type {
+ OFPUTIL_METER_FEATURES,
+ OFPUTIL_METER_CONFIG,
+ OFPUTIL_METER_STATS
+};
+
+struct ofpbuf *ofputil_encode_meter_request(enum ofp_version,
+ enum ofputil_meter_request_type,
+ uint32_t meter_id);
+
+int ofputil_decode_meter_stats(struct ofpbuf *msg,
+ struct ofputil_meter_stats *ms,
+ struct ofpbuf *bands);
+
+int ofputil_decode_meter_config(struct ofpbuf *msg,
+ struct ofputil_meter_config *mc,
+ struct ofpbuf *bands);
+
+/* Type for meter_id in ofproto provider interface, UINT32_MAX if none */
+typedef struct { uint32_t uint32; } ofproto_meter_id;
+
/* Abstract ofp_role_request and reply. */
struct ofputil_role_request {
enum ofp12_controller_role role;
flow->metadata |= metadata->metadata & metadata->mask;
break;
+ case OFPACT_METER:
+ /* Not implemented yet. */
+ break;
+
case OFPACT_GOTO_TABLE: {
/* It is assumed that goto-table is the last action. */
struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a);
])
AT_CLEANUP
+AT_SETUP([OFPT_METER_MOD request - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 1d 00 20 00 00 00 02 00 00 00 0d 00 00 00 05 \
+00 01 00 10 00 00 04 00 00 00 00 80 00 00 00 00 \
+"], [0], [dnl
+OFPT_METER_MOD (OF1.3) (xid=0x2): ADD meter=5 kbps burst stats bands=
+type=drop rate=1024 burst_size=128
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_METER request - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "041200180000000200090000000000000000000100000000"], [0], [dnl
+OFPST_METER request (OF1.3) (xid=0x2): meter=1
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_METER_CONFIG request - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "0412001800000002000a0000000000000000000100000000"], [0], [dnl
+OFPST_METER_CONFIG request (OF1.3) (xid=0x2): meter=1
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_METER_FEATURES request - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "0412001000000002000b000000000000"], [0], [dnl
+OFPST_METER_FEATURES request (OF1.3) (xid=0x2):
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_METER_FEATURES reply - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 13 00 20 00 00 00 02 00 0b 00 00 00 00 00 00 \
+00 01 00 00 00 00 00 03 00 00 00 0F 10 02 00 00 \
+"], [0], [dnl
+OFPST_METER_FEATURES reply (OF1.3) (xid=0x2):
+max_meter:65536 max_bands:16 max_color:2
+band_types: drop dscp_remark
+capabilities: kbps pktps burst stats
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_METER_CONFIG reply - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 13 00 50 00 00 00 02 00 0a 00 00 00 00 00 00 \
+00 28 00 05 00 00 00 01 \
+00 01 00 10 00 01 00 00 00 00 05 00 00 00 00 00 \
+00 02 00 10 00 10 00 00 00 00 f0 00 00 00 00 00 \
+00 18 00 09 00 00 00 02 \
+00 01 00 10 00 02 00 00 00 00 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPST_METER_CONFIG reply (OF1.3) (xid=0x2):
+meter=1 kbps burst bands=
+type=drop rate=65536 burst_size=1280
+type=dscp_remark rate=1048576 burst_size=61440 prec_level=0
+
+meter=2 kbps stats bands=
+type=drop rate=131072
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_METER reply - OF1.3])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 13 00 90 00 00 00 02 00 09 00 00 00 00 00 00 \
+00 00 00 01 00 48 00 00 00 00 00 00 00 00 00 05 \
+00 00 00 00 00 00 10 00 00 00 00 00 00 02 30 00 \
+00 00 01 8a 9a 6e 23 44 \
+00 00 00 00 00 00 00 7e 00 00 00 00 00 00 34 33 \
+00 00 00 00 00 00 00 e7 00 00 00 00 00 00 94 2e \
+00 00 00 02 00 38 00 00 00 00 00 00 00 00 00 02 \
+00 00 00 00 00 00 02 00 00 00 00 00 00 00 30 00 \
+00 00 01 87 9a 23 6e 44 \
+00 00 00 00 00 00 00 2a 00 00 00 00 00 00 04 33 \
+"], [0], [dnl
+OFPST_METER reply (OF1.3) (xid=0x2):
+meter:1 flow_count:5 packet_in_count:4096 byte_in_count:143360 duration:394.2590909252s bands:
+0: packet_count:126 byte_count:13363
+1: packet_count:231 byte_count:37934
+
+meter:2 flow_count:2 packet_in_count:512 byte_in_count:12288 duration:391.2586013252s bands:
+0: packet_count:42 byte_count:1075
+])
+AT_CLEANUP
+
AT_SETUP([OFPT_BARRIER_REQUEST - OF1.0])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print '01 12 00 08 00 00 00 01'], [0], [dnl