ofp-util: Support for OpenFlow 1.3 meters.
authorJarno Rajahalme <jarno.rajahalme@nsn.com>
Thu, 20 Jun 2013 14:26:18 +0000 (17:26 +0300)
committerBen Pfaff <blp@nicira.com>
Tue, 25 Jun 2013 17:34:09 +0000 (10:34 -0700)
Signed-off-by: Jarno Rajahalme <jarno.rajahalme@nsn.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
12 files changed:
include/openflow/openflow-1.3.h
include/openflow/openflow-common.h
lib/ofp-actions.c
lib/ofp-actions.h
lib/ofp-msgs.h
lib/ofp-parse.c
lib/ofp-parse.h
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.h
ofproto/ofproto-dpif-xlate.c
tests/ofp-print.at

index 231bba9..1071d3d 100644 (file)
@@ -402,7 +402,7 @@ struct ofp13_meter_stats {
     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);
index 3cc22c9..ee79136 100644 (file)
@@ -352,6 +352,7 @@ enum ofp_flow_removed_reason {
     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 */
index 5339811..99494a2 100644 (file)
@@ -1087,6 +1087,16 @@ ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
         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;
@@ -1229,6 +1239,7 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports)
 
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_WRITE_METADATA:
+    case OFPACT_METER:
     case OFPACT_GOTO_TABLE:
         return 0;
 
@@ -1272,7 +1283,9 @@ ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len)
     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;
@@ -1552,6 +1565,7 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_SET_L4_DST_PORT:
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_GOTO_TABLE:
+    case OFPACT_METER:
         NOT_REACHED();
     }
 }
@@ -1644,6 +1658,7 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_PUSH_VLAN:
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_GOTO_TABLE:
+    case OFPACT_METER:
         /* XXX */
         break;
 
@@ -1816,6 +1831,7 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
 
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_GOTO_TABLE:
+    case OFPACT_METER:
         NOT_REACHED();
 
     case OFPACT_CONTROLLER:
@@ -1896,6 +1912,13 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
             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;
@@ -1965,6 +1988,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
     case OFPACT_SAMPLE:
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_GOTO_TABLE:
+    case OFPACT_METER:
     default:
         return false;
     }
@@ -2291,6 +2315,12 @@ ofpact_format(const struct ofpact *a, struct ds *s)
                           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;
     }
 }
 
index af63833..799f64c 100644 (file)
     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. */
@@ -374,6 +375,14 @@ struct ofpact_metadata {
     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. */
@@ -601,6 +610,10 @@ void ofpact_pad(struct ofpbuf *);
  * 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")                            \
@@ -639,6 +652,7 @@ ofpact_is_instruction(const struct ofpact *a)
 {
     /* XXX Write-Actions */
     return a->type == OFPACT_CLEAR_ACTIONS
+        || a->type == OFPACT_METER
         || a->type == OFPACT_WRITE_METADATA
         || a->type == OFPACT_GOTO_TABLE;
 }
index 66ec448..2501e38 100644 (file)
@@ -217,7 +217,7 @@ enum ofpraw {
     /* 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. */
@@ -315,13 +315,13 @@ enum ofpraw {
     /* 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. */
index 1890050..b0f4d93 100644 (file)
@@ -45,14 +45,14 @@ static void ofp_fatal(const char *flow, bool verbose, const char *format, ...)
     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
@@ -705,6 +705,10 @@ parse_named_instruction(enum ovs_instruction_type type,
         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;
@@ -715,7 +719,7 @@ parse_named_instruction(enum ovs_instruction_type type,
         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;
     }
     }
@@ -948,7 +952,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
             }
 
             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",
@@ -1025,6 +1029,198 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     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
@@ -1074,7 +1270,7 @@ parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
             }
 
             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)) {
index d2d3c3c..6ee25a4 100644 (file)
@@ -28,6 +28,7 @@ struct ofpbuf;
 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);
@@ -45,6 +46,9 @@ void parse_ofpacts(const char *, struct ofpbuf *ofpacts);
 
 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 *);
 
index 42fd9a6..76bd09c 100644 (file)
@@ -878,6 +878,8 @@ ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason,
         return "group_delete";
     case OFPRR_EVICTION:
         return "eviction";
+    case OFPRR_METER_DELETE:
+        return "meter_delete";
     default:
         snprintf(reasonbuf, bufsize, "%d", (int) reason);
         return reasonbuf;
@@ -954,6 +956,236 @@ ofp_print_port_mod(struct ds *string, const struct ofp_header *oh)
     }
 }
 
+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)
 {
@@ -1910,19 +2142,12 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
     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);
@@ -1980,6 +2205,10 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         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;
@@ -1989,8 +2218,30 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         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;
 
index de73eba..6c58415 100644 (file)
@@ -1631,6 +1631,368 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
     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)
index eaa3af2..57d35c4 100644 (file)
@@ -514,6 +514,93 @@ enum ofperr ofputil_decode_port_mod(const struct ofp_header *,
 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;
index a52a8cf..36f54ee 100644 (file)
@@ -1739,6 +1739,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             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);
index caf0ecc..b3e3eb5 100644 (file)
@@ -1581,6 +1581,95 @@ OFPST_PORT_DESC reply (xid=0x0):
 ])
 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