[ovs-dev] [PATCH] [RFC] Add OpenFlow 1.3 protocol support for meters.

Ben Pfaff blp at nicira.com
Thu Jun 20 20:55:24 UTC 2013


From: Neil Zhu <zhuj at centecnetworks.com>

Signed-off-by: Neil Zhu <zhuj at centecnetworks.com>
Signed-off-by: Ben Pfaff <blp at nicira.com>
---
This is the first of a series of Open vSwitch features contributed
by Centec Networks.  Other features will include groups, the
"table mod" request, and some queue related features.

This feature, like the others, does not include Linux kernel
datapath support, because Centec produces ASIC-based implementations.

This patch is not completely ready to go, but I am posting it now
to get the ball rolling.

 lib/automake.mk              |    2 +
 lib/ihash.c                  |  253 ++++++++++++++++++++
 lib/ihash.h                  |   91 +++++++
 lib/ofp-actions.c            |   34 +++
 lib/ofp-actions.h            |   15 +-
 lib/ofp-msgs.h               |    4 +-
 lib/ofp-parse.c              |  248 +++++++++++++++++++
 lib/ofp-parse.h              |    8 +
 lib/ofp-print.c              |  232 ++++++++++++++++++-
 lib/ofp-util.c               |  403 +++++++++++++++++++++++++++++++
 lib/ofp-util.h               |  107 +++++++++
 ofproto/ofproto-dpif-xlate.c |    4 +
 ofproto/ofproto-dpif.c       |    9 +
 ofproto/ofproto-provider.h   |   27 ++
 ofproto/ofproto.c            |  539 +++++++++++++++++++++++++++++++++++++++++-
 utilities/ovs-ofctl.c        |  201 ++++++++++++++++
 16 files changed, 2162 insertions(+), 15 deletions(-)
 create mode 100644 lib/ihash.c
 create mode 100644 lib/ihash.h

diff --git a/lib/automake.mk b/lib/automake.mk
index eec71dd..e12179a 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -63,6 +63,8 @@ lib_libopenvswitch_a_SOURCES = \
 	lib/hmap.h \
 	lib/hmapx.c \
 	lib/hmapx.h \
+	lib/ihash.c \
+	lib/ihash.h \
 	lib/jhash.c \
 	lib/jhash.h \
 	lib/json.c \
diff --git a/lib/ihash.c b/lib/ihash.c
new file mode 100644
index 0000000..8660736
--- /dev/null
+++ b/lib/ihash.c
@@ -0,0 +1,253 @@
+/** Copyright (C) 2011, 2012 CentecNetworks, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file
+ * @brief This file implements a hashmap database (with integer key) and its operations
+ */
+
+#include <config.h>
+#include "ihash.h"
+#include "hash.h"
+#include "util.h"
+#include "vlog.h"
+
+extern int ctc_cli_out_ofp (const char *fmt, ...);
+
+VLOG_DEFINE_THIS_MODULE(ofproto_ihash);
+
+static struct ihash_node *
+ihash_find__(const struct ihash *, const uint32_t key, size_t hash);
+
+static size_t
+hash_key(uint32_t key)
+{
+    return hash_int(key, 0);
+}
+
+void
+ihash_init(struct ihash *sh)
+{
+    hmap_init(&sh->map);
+}
+
+void
+ihash_destroy(struct ihash *sh)
+{
+    if (sh)
+    {
+        ihash_clear(sh);
+        hmap_destroy(&sh->map);
+    }
+}
+
+/* Like ihash_destroy(), but also free() each node's 'data'. */
+void
+ihash_destroy_free_data(struct ihash *sh)
+{
+    if (sh)
+    {
+        ihash_clear_free_data(sh);
+        hmap_destroy(&sh->map);
+    }
+}
+
+void
+ihash_clear(struct ihash *sh)
+{
+    struct ihash_node *node, *next;
+
+    IHASH_FOR_EACH_SAFE (node, next, sh)
+    {
+        hmap_remove(&sh->map, &node->node);
+        /* free(node->name); */
+        free(node);
+    }
+}
+
+/* Like ihash_clear(), but also free() each node's 'data'. */
+void
+ihash_clear_free_data(struct ihash *sh)
+{
+    struct ihash_node *node, *next;
+
+    IHASH_FOR_EACH_SAFE (node, next, sh)
+    {
+        hmap_remove(&sh->map, &node->node);
+        free(node->data);
+        /* free(node->name); */
+        free(node);
+    }
+}
+
+bool
+ihash_is_empty(const struct ihash *ihash)
+{
+    return hmap_is_empty(&ihash->map);
+}
+
+size_t
+ihash_count(const struct ihash *ihash)
+{
+    return hmap_count(&ihash->map);
+}
+
+static struct ihash_node *
+ihash_add_nocopy__(struct ihash *sh, uint32_t key, const void *data, size_t hash)
+{
+    struct ihash_node *node = xmalloc(sizeof *node);
+    node->key = key;
+    node->data = (void *) data;
+    hmap_insert(&sh->map, &node->node, hash);
+    return node;
+}
+
+/* It is the caller's responsibility to avoid duplicate names, if that is
+ * desirable. */
+struct ihash_node *
+ihash_add_nocopy(struct ihash *sh, uint32_t key, const void *data)
+{
+    return ihash_add_nocopy__(sh, key, data, hash_key(key));
+}
+
+/* It is the caller's responsibility to avoid duplicate key, if that is
+ * desirable. */
+struct ihash_node *
+ihash_add(struct ihash *sh, const uint32_t key, const void *data)
+{
+    return ihash_add_nocopy(sh, key, data);
+}
+
+bool
+ihash_add_once(struct ihash *sh, const uint32_t key, const void *data)
+{
+    if (!ihash_find(sh, key))
+    {
+        ihash_add(sh, key, data);
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+void
+ihash_add_assert(struct ihash *sh, const uint32_t key, const void *data)
+{
+    bool added OVS_UNUSED = ihash_add_once(sh, key, data);
+    ovs_assert(added);
+}
+
+/* Deletes 'node' from 'sh' and frees the node's name.  The caller is still
+ * responsible for freeing the node's data, if necessary. */
+void
+ihash_delete(struct ihash *sh, struct ihash_node *node)
+{
+    /* free(ihash_steal(sh, node)); */
+    ihash_steal(sh, node);
+}
+
+/* Deletes 'node' from 'sh'.  Neither the node's name nor its data is freed;
+ * instead, ownership is transferred to the caller.  Returns the node's
+ * name. */
+uint32_t
+ihash_steal(struct ihash *sh, struct ihash_node *node)
+{
+    uint32_t key = node->key;
+
+    hmap_remove(&sh->map, &node->node);
+    free(node);
+    return key;
+}
+
+bool
+ihash_contains(const struct ihash *sh, const uint32_t key)
+{
+    if (ihash_find(sh, key) == NULL)
+    {
+        return false;
+    }
+    return true;
+}
+
+static struct ihash_node *
+ihash_find__(const struct ihash *sh, const uint32_t key, size_t hash)
+{
+    struct ihash_node *node;
+
+    HMAP_FOR_EACH_WITH_HASH (node, node, hash, &sh->map)
+    {
+        if (node->key == key)
+        {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+/* If there are duplicates, returns a random element. */
+struct ihash_node *
+ihash_find(const struct ihash *sh, const uint32_t key)
+{
+    return ihash_find__(sh, key, hash_key(key));
+}
+
+static int
+compare_ints(const void *a_, const void *b_)
+{
+    return *((uint32_t*) a_) - *((uint32_t*) b_);
+}
+
+const struct ihash_node **
+ihash_sort(const struct ihash *sh)
+{
+    if (ihash_is_empty(sh))
+    {
+        return NULL;
+    }
+    else
+    {
+        const struct ihash_node **nodes;
+        struct ihash_node *node;
+        size_t i, n;
+
+        n = ihash_count(sh);
+        nodes = xmalloc(n * sizeof *nodes);
+        i = 0;
+        IHASH_FOR_EACH (node, sh)
+        {
+            nodes[i++] = node;
+        }ovs_assert(i == n);
+
+        qsort(nodes, n, sizeof *nodes, compare_ints);
+
+        return nodes;
+    }
+}
+
+void
+ihash_print_value_int(struct ihash *sh)
+{
+    uint32_t key = 0;
+    uint32_t data = 0;
+    int i = 0;
+    struct ihash_node *node, *next;
+    IHASH_FOR_EACH_SAFE (node, next, sh)
+    {
+        key = node->key;
+        data = *((uint32_t *) (node->data));
+        i++;
+    }
+}
+
diff --git a/lib/ihash.h b/lib/ihash.h
new file mode 100644
index 0000000..04176a9
--- /dev/null
+++ b/lib/ihash.h
@@ -0,0 +1,91 @@
+/** Copyright (C) 2011, 2012 CentecNetworks, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file
+ * @brief This file is the header file of ihash.c
+ */
+
+#ifndef IHASH_H
+#define IHASH_H 1
+
+#include "hmap.h"
+#include <stdint.h>
+
+#ifdef  __cplusplus
+extern "C"
+{
+#endif
+
+    struct ihash_node
+    {
+        struct hmap_node node;
+        uint32_t key;
+        void *data;
+    };
+
+    struct ihash
+    {
+        struct hmap map;
+    };
+
+#define IHASH_INITIALIZER(IHASH) { HMAP_INITIALIZER(&(IHASH)->map) }
+
+#define IHASH_FOR_EACH(IHASH_NODE, IHASH) \
+    HMAP_FOR_EACH (IHASH_NODE, node, &(IHASH)->map)
+
+#define IHASH_FOR_EACH_SAFE(IHASH_NODE, NEXT, IHASH) \
+    HMAP_FOR_EACH_SAFE (IHASH_NODE, NEXT, node, &(IHASH)->map)
+
+    void
+    ihash_init(struct ihash *);
+    void
+    ihash_destroy(struct ihash *);
+    void
+    ihash_destroy_free_data(struct ihash *);
+    void
+    ihash_clear(struct ihash *);
+    void
+    ihash_clear_free_data(struct ihash *);
+    bool
+    ihash_is_empty(const struct ihash *);
+    size_t
+    ihash_count(const struct ihash *);
+    struct ihash_node *
+    ihash_add(struct ihash *, const uint32_t, const void *);
+    struct ihash_node *
+    ihash_add_nocopy(struct ihash *, uint32_t, const void *);
+    bool
+    ihash_add_once(struct ihash *, const uint32_t, const void *);
+    void
+    ihash_add_assert(struct ihash *, const uint32_t, const void *);
+    struct ihash_node *
+    ihash_find(const struct ihash *, const uint32_t);
+    void
+    ihash_delete(struct ihash *, struct ihash_node *);
+    uint32_t
+    ihash_steal(struct ihash *, struct ihash_node *);
+    bool
+    ihash_contains(const struct ihash *, const uint32_t);
+    const struct ihash_node **
+    ihash_sort(const struct ihash *);
+    void
+    ihash_print_value_int(struct ihash *);
+    void
+    ihash_print_value_ivec(struct ihash *);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* ihash.h */
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 45d8601..5e08235 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -1087,6 +1087,15 @@ 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 = instruction_get_OFPIT13_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;
@@ -1231,6 +1240,9 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow,
     case OFPACT_WRITE_METADATA:
     case OFPACT_GOTO_TABLE:
         return 0;
+        
+    case OFPACT_METER:
+        return 0;
 
     default:
         NOT_REACHED();
@@ -1548,6 +1560,9 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
         ofpact_sample_to_nxast(ofpact_get_SAMPLE(a), out);
         break;
 
+    case OFPACT_METER:
+        NOT_REACHED();
+
     case OFPACT_OUTPUT:
     case OFPACT_ENQUEUE:
     case OFPACT_SET_VLAN_VID:
@@ -1658,6 +1673,9 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
         /* XXX */
         break;
 
+    case OFPACT_METER:
+        break;
+
     case OFPACT_CONTROLLER:
     case OFPACT_OUTPUT_REG:
     case OFPACT_BUNDLE:
@@ -1829,6 +1847,9 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_GOTO_TABLE:
         NOT_REACHED();
 
+    case OFPACT_METER:
+        NOT_REACHED();
+
     case OFPACT_CONTROLLER:
     case OFPACT_OUTPUT_REG:
     case OFPACT_BUNDLE:
@@ -1893,6 +1914,11 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
 
         if (a->type == OFPACT_CLEAR_ACTIONS) {
             instruction_put_OFPIT11_CLEAR_ACTIONS(openflow);
+        } else if (a->type == OFPACT_METER) {
+            struct ofp13_instruction_meter *oim;
+
+            oim = instruction_put_OFPIT13_METER(openflow);
+            oim->meter_id = htonl(ofpact_get_METER(a)->meter_id);
         } else if (a->type == OFPACT_GOTO_TABLE) {
             struct ofp11_instruction_goto_table *oigt;
 
@@ -1976,6 +2002,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;
     }
@@ -2302,6 +2329,13 @@ 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;
     }
 }
 
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index 4fc40b3..c880fba 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -100,7 +100,8 @@
     /* XXX Write-Actions */                                         \
     DEFINE_OFPACT(WRITE_METADATA,  ofpact_metadata,      ofpact)    \
     DEFINE_OFPACT(CLEAR_ACTIONS,   ofpact_null,          ofpact)    \
-    DEFINE_OFPACT(GOTO_TABLE,      ofpact_goto_table,    ofpact)
+    DEFINE_OFPACT(GOTO_TABLE,      ofpact_goto_table,    ofpact)    \
+    DEFINE_OFPACT(METER,           ofpact_meter,         ofpact)
 
 /* enum ofpact_type, with a member OFPACT_<ENUM> for each action. */
 enum OVS_PACKED_ENUM ofpact_type {
@@ -481,6 +482,11 @@ struct ofpact_goto_table {
     uint8_t table_id;
 };
 
+struct ofpact_meter {
+    struct ofpact ofpact;
+    uint32_t meter_id;
+};
+
 /* Converting OpenFlow to ofpacts. */
 enum ofperr ofpacts_pull_openflow10(struct ofpbuf *openflow,
                                     unsigned int actions_len,
@@ -601,6 +607,10 @@ void ofpact_pad(struct ofpbuf *);
  * It is enforced on parser from text string.
  */
 #define OVS_INSTRUCTIONS                                    \
+    DEFINE_INST(OFPIT13_METER,                              \
+                ofp13_instruction_meter,          true,     \
+                "meter")                                    \
+                                                            \
     DEFINE_INST(OFPIT11_APPLY_ACTIONS,                      \
                 ofp11_instruction_actions,        true,     \
                 "apply_actions")                            \
@@ -640,7 +650,8 @@ ofpact_is_instruction(const struct ofpact *a)
     /* XXX Write-Actions */
     return a->type == OFPACT_CLEAR_ACTIONS
         || a->type == OFPACT_WRITE_METADATA
-        || a->type == OFPACT_GOTO_TABLE;
+        || a->type == OFPACT_GOTO_TABLE
+        || a->type == OFPACT_METER;
 }
 
 const char *ofpact_instruction_name_from_type(enum ovs_instruction_type type);
diff --git a/lib/ofp-msgs.h b/lib/ofp-msgs.h
index 66ec448..365d6d7 100644
--- a/lib/ofp-msgs.h
+++ b/lib/ofp-msgs.h
@@ -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, uint16_t[]. */
     OFPRAW_OFPT13_METER_MOD,
 
 /* Standard statistics. */
@@ -315,7 +315,7 @@ 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[]. */
     OFPRAW_OFPST13_METER_REPLY,
 
     /* OFPST 1.3+ (10): struct ofp13_meter_multipart_request. */
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 1890050..8f0c54e 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -718,6 +718,16 @@ parse_named_instruction(enum ovs_instruction_type type,
         ogt->table_id = str_to_table_id(table_s);
         break;
     }
+
+    case OVSINST_OFPIT13_METER: {
+        struct ofpact_meter *om = ofpact_put_METER(ofpacts);
+        char *meter_s = strsep(&arg, ",");
+        if (!meter_s || !meter_s[0]) {
+            ovs_fatal(0, "instruction meter needs meter id");
+        }
+        om->meter_id = str_to_u32(meter_s);
+        break;
+    }
     }
 
     /* If write_metadata is specified as an action AND an instruction, ofpacts
@@ -1241,3 +1251,241 @@ exit:
     }
     return error;
 }
+
+void
+parse_ofp_meter_mod_file(const char *file_name, uint16_t command,
+                        struct ofputil_meter_mod **mms, size_t *n_mms)
+{
+    size_t allocated_mms;
+    FILE *stream;
+    struct ds s;
+
+    stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r");
+    if (stream == NULL) {
+        ovs_fatal(errno, "%s: open", file_name);
+    }
+
+    allocated_mms = *n_mms;
+    ds_init(&s);
+    while (!ds_get_preprocessed_line(&s, stream)) {
+        if (*n_mms >= allocated_mms) {
+            *mms = x2nrealloc(*mms, &allocated_mms, sizeof **mms);
+        }
+        parse_ofp_meter_mod_str(&(*mms)[*n_mms], ds_cstr(&s), command, false);
+        *n_mms += 1;
+    }
+    ds_destroy(&s);
+
+    if (stream != stdin) {
+        fclose(stream);
+    }
+}
+
+static int
+parse_meter_flags_str(const char* str, uint16_t* flags)
+{
+    while(*str)
+    {
+        if (!strncasecmp(str,"KBPS", 4)) {
+            *flags |= OFPMF13_KBPS;
+            str += 4;
+        } else if (!strncasecmp(str,"PKTPS", 5)) {
+            *flags |= OFPMF13_PKTPS;
+            str += 5;
+        } else if (!strncasecmp(str,"BURST", 5)) {
+            *flags |= OFPMF13_BURST;
+            str += 5;
+        } else if (!strncasecmp(str,"STATS", 5)) {
+            *flags |= OFPMF13_STATS;
+            str += 5;
+        } else if (*str == '-' || *str == '_') {
+            str++;
+        } else {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static void
+parse_ofp_meter_str(struct ofputil_meter_mod *mm, int command, const char *str_,
+              bool verbose)
+{
+    enum {
+        F_METER_ID  = 1 << 0,
+        F_BANDS     = 1 << 1
+    } fields;
+#define INVALID_RATE  0xffffffff
+#define INVALID_BURST 0xffffffff
+
+    char *string = NULL;
+    char *save_ptr = NULL;
+    char *name;
+    size_t band = 0, band_i = 0;
+    struct ofp13_meter_band_drop bands[OFP_MAX_BAND_PER_METER + 1];
+
+    switch (command) {
+    case OFPMC13_ADD:
+        fields = F_METER_ID | F_BANDS;
+        break;
+
+    case OFPMC13_MODIFY:
+        fields = F_METER_ID | F_BANDS;
+        break;
+
+    case OFPMC13_DELETE:
+        fields = F_METER_ID;
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+
+    memset(&bands, 0x0, sizeof(bands));
+    memset(mm, 0x0, sizeof(*mm));
+    mm->command = command;
+    mm->meter_id = OFPM13_ALL;
+
+    if (OFPMC13_DELETE == mm->command && !strlen(str_)) {
+        mm->meter_id = OFPM13_ALL;
+        return;
+    }
+    mm->flags = 0;
+
+    string = xstrdup(str_);
+
+    for (band_i = 1; band_i <= OFP_MAX_BAND_PER_METER; band_i++) {
+        bands[band_i].type = htons(OFPMBT13_EXPERIMENTER);
+        bands[band_i].rate = htonl(INVALID_RATE);
+        bands[band_i].burst_size = htonl(INVALID_BURST);
+    }
+
+    for (name = strtok_r(string, "=, \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);
+        }
+
+        /*modified by liul for bug 22403, 2013-03-04*/
+        /*command "delete" is only need "meter_id"*/
+        if (OFPMC13_DELETE == mm->command) {
+            if (!strcasecmp(name, "meter_id")) {
+                if(!strcasecmp(value, "all")) {
+                    mm->meter_id = OFPM13_ALL;
+                } else {
+                    mm->meter_id = str_to_u32(value);
+                    if (!mm->meter_id) {
+                        ofp_fatal(str_, verbose, "invalid meter_id %"PRIu32"", mm->meter_id);
+                    }
+                }
+            } else {
+                ofp_fatal(str_, verbose, "only meter_id is needed");
+            }
+            return;
+        } 
+
+        if (!strcasecmp(name, "meter_id")) {
+            if(!strcasecmp(value, "all")) {
+                mm->meter_id = OFPM13_ALL;
+            } else {
+                mm->meter_id = str_to_u32(value);
+                if (!mm->meter_id) {
+                    ofp_fatal(str_, verbose, "invalid meter_id %"PRIu32"", mm->meter_id);
+                }
+            }
+        } else if (!strcasecmp(name, "flags")) {
+            if (parse_meter_flags_str(value, &mm->flags)) {
+                ofp_fatal(str_, verbose, "invalid meter flags %s", value);
+            }
+        } else if (!strcasecmp(name, "band_type")){
+            if (!mm->flags) {
+                ofp_fatal(str_, verbose, "must specify flags before band_type");
+            }
+            if (band >= OFP_MAX_BAND_PER_METER) {
+                ofp_fatal(str_, verbose, "Only %"PRIu32" bands per meter are supported.", OFP_MAX_BAND_PER_METER);
+            }
+            if (!strcasecmp(value, "drop")) {
+                bands[++band].type = htons(OFPMBT13_DROP);
+            } else {
+                ofp_fatal(str_, verbose, "invalid band type %s", value);
+            }
+        } else if (!strcasecmp(name, "rate")) {
+            if (band < 1) {
+                ofp_fatal(str_, verbose, "must specify a band type before rate");
+            }
+            bands[band].rate = htonl(str_to_u32(value));
+        } else if (!strcasecmp(name, "burst")) {
+            if (band < 1) {
+                ofp_fatal(str_, verbose, "must specify a band type before burst");
+            }
+            if (!(mm->flags & OFPMF13_BURST)) {
+                ofp_fatal(str_, verbose, "BURST is not specified in flags");
+            }
+            bands[band].burst_size = htonl(str_to_u32(value));
+        } else {
+            ofp_fatal(str_, verbose, "unknown keyword %s", name);
+        }
+    }
+    if ( fields & F_METER_ID) {
+        if (!mm->meter_id) {
+            ofp_fatal(str_, verbose, "must specify a meter_id");
+        }
+    }
+    if ( fields & F_BANDS) {
+        if (!mm->flags) {
+            ofp_fatal(str_, verbose, "must specify meter flags");
+        }
+        if (!band) {
+            ofp_fatal(str_, verbose, "must specify a band type");
+        }
+        for (band_i = 1; band_i <= band; band_i++) {
+            if (htons(OFPMBT13_EXPERIMENTER) == bands[band_i].type) {
+                ofp_fatal(str_, verbose, "must specify a band type");
+            }
+            if (htonl(INVALID_RATE) == bands[band_i].rate) {
+                ofp_fatal(str_, verbose, "must specify a band rate");
+            }
+            if ((mm->flags & OFPMF13_BURST) &&
+                htonl(INVALID_BURST) == bands[band_i].burst_size) {
+                ofp_fatal(str_, verbose, "must specify a band burst");
+            }
+
+            if (bands[band_i].type == htons(OFPMBT13_DROP)) {
+                bands[band_i].len = htons(sizeof(struct ofp13_meter_band_drop));
+            }
+        }
+        if (band) {
+            mm->n_bands = band;
+            mm->bands = malloc(sizeof(struct ofp13_meter_band_drop) * band);
+            memcpy(mm->bands, &bands[1], sizeof(struct ofp13_meter_band_drop) * band);
+        }
+    } else {
+        if (band || mm->flags) {
+            ofp_fatal(str_, verbose, "only meter_id is needed");
+        }
+    }
+
+    free(string);
+}
+
+void
+parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *string,
+        uint16_t command, bool verbose)
+{
+    parse_ofp_meter_str(mm, command, string, verbose);
+}
+
+void
+parse_ofp_meter_request_str(struct ofputil_meter_request *mr, char *string)
+{
+    struct ofputil_meter_mod mm;
+
+    memset(&mm, 0, sizeof(mm));
+    parse_ofp_meter_str(&mm, OFPMC13_DELETE, string, false);
+    mr->meter_id = mm.meter_id;
+}
diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h
index d2d3c3c..4620eae 100644
--- a/lib/ofp-parse.h
+++ b/lib/ofp-parse.h
@@ -28,6 +28,9 @@ struct ofpbuf;
 struct ofputil_flow_mod;
 struct ofputil_flow_monitor_request;
 struct ofputil_flow_stats_request;
+/* Added by yanxa, for bug 21920, 2012-1-5 */
+struct ofputil_meter_mod;
+struct ofputil_meter_request;
 
 void parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
                    bool verbose);
@@ -48,4 +51,9 @@ char *parse_ofp_exact_flow(struct flow *, const char *);
 void parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
                                 const char *);
 
+void parse_ofp_meter_mod_file(const char *file_name, uint16_t command,
+                        struct ofputil_meter_mod **mms, size_t *n_mms);
+void parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *string,
+                            uint16_t command, bool verbose);
+void parse_ofp_meter_request_str(struct ofputil_meter_request *mr, char *string);
 #endif /* ofp-parse.h */
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 42fd9a6..7e35ef2 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -1896,6 +1896,201 @@ ofp_print_not_implemented(struct ds *string)
     ds_put_cstr(string, "NOT IMPLEMENTED YET!\n");
 }
 
+/* Meter */
+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");
+    }
+}
+
+static void
+ofp_print_meter_mod(struct ds *s, const struct ofp_header *oh)
+{
+    size_t band_i;
+    struct ofputil_meter_mod mm;
+    int error;
+
+    memset(&mm, 0, sizeof(mm));
+    error = ofputil_decode_meter_mod(&mm, oh);
+    if (error) {
+        ofp_print_error(s, error);
+        return;
+    }
+
+    ds_put_char(s, '\n');
+
+    ds_put_char(s, ' ');
+    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:%"PRIu16"", mm.command);
+    }
+    ds_put_char(s, ' ');
+
+    ds_put_format(s, "meter_id=%"PRIu32"", mm.meter_id);
+    ds_put_format(s, ",flags=");
+    ofp_print_meter_flags(s, mm.flags);
+
+    for (band_i = 0; band_i < mm.n_bands; band_i++) {
+        ds_put_format(s, ",band_type=%s,", ntohs(mm.bands[band_i].type) == OFPMBT13_DROP ? "DROP" : "KNOWN");
+        ds_put_format(s, "rate=%"PRIu32"", ntohl(mm.bands[band_i].rate));
+
+        if (mm.flags & OFPMF13_BURST) {
+            ds_put_format(s, ",burst=%"PRIu32"", ntohl(mm.bands[band_i].burst_size));
+        }
+    }
+}
+
+static void
+ofp_print_meter_request(struct ds *s, const struct ofp_header *oh)
+{
+    struct ofputil_meter_request omr;
+    enum ofperr error;
+
+    error = ofputil_decode_meter_request(&omr, oh);
+    if (error) {
+        ofp_print_error(s, error);
+        return;
+    }
+
+    ds_put_char(s, ' ');
+    ds_put_cstr(s, "METER_STATS");
+    ds_put_char(s, ' ');
+    if (OFPM13_ALL == omr.meter_id) {
+        ds_put_format(s, "meter_id=all");
+    } else {
+        ds_put_format(s, "meter_id=%"PRIu32"", omr.meter_id);
+    }
+}
+
+static void
+ofp_print_meter_reply(struct ds *s, const struct ofp_header *oh)
+{
+    struct ofpbuf b;
+    size_t band_i;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    for (;;) {
+        struct ofputil_meter_stats_reply omsr;
+        enum ofperr error;
+
+        error = ofputil_decode_meter_reply(&omsr, &b);
+        if (error) {
+            if (error != EOF) {
+                ofp_print_error(s, error);
+            }
+            break;
+        }
+        ds_put_char(s, '\n');
+
+        ds_put_char(s, ' ');
+        ds_put_format(s, "meter_id=%"PRIu32",", omsr.meter_id);
+
+        ds_put_cstr(s, "duration=");
+        ofp_print_duration(s, omsr.duration_sec, omsr.duration_nsec);
+        ds_put_format(s, ",flow_count=%"PRIu32",", omsr.flow_count);
+        ds_put_format(s, "packet_in_count=%"PRIu64",", omsr.packet_in_count);
+        ds_put_format(s, "byte_in_count=%"PRIu64",", omsr.byte_in_count);
+
+        for (band_i = 0; band_i < omsr.n_bands; band_i++) {
+            ds_put_format(s, "packet_band_count=%"PRIu64",", ntohll(omsr.bands_stats[band_i].packet_band_count));
+            ds_put_format(s, "byte_band_count=%"PRIu64"", ntohll(omsr.bands_stats[band_i].byte_band_count));
+        }
+    }
+}
+
+static void
+ofp_print_meter_config_request(struct ds *s, const struct ofp_header *oh)
+{
+    struct ofputil_meter_request omr;
+    enum ofperr error;
+
+    error = ofputil_decode_meter_request(&omr, oh);
+    if (error) {
+        ofp_print_error(s, error);
+        return;
+    }
+
+    ds_put_char(s, ' ');
+    ds_put_cstr(s, "METER_CONFIG");
+    ds_put_char(s, ' ');
+    if (OFPM13_ALL == omr.meter_id) {
+        ds_put_format(s, "meter_id=all");
+    } else {
+        ds_put_format(s, "meter_id=%"PRIu32"", omr.meter_id);
+    }
+}
+
+static void
+ofp_print_meter_config_reply(struct ds *s, const struct ofp_header *oh)
+{
+    struct ofpbuf b;
+    size_t band_i;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    for (;;) {
+        struct ofputil_meter_config_reply omcr;
+        enum ofperr error;
+
+        error = ofputil_decode_meter_config_reply(&omcr, &b);
+        if (error) {
+            if (error != EOF) {
+                ofp_print_error(s, error);
+            }
+            break;
+        }
+
+        ds_put_char(s, '\n');
+        ds_put_char(s, ' ');
+        ds_put_format(s, "meter_id=%"PRIu32",flags=", omcr.meter_id);
+        ofp_print_meter_flags(s, omcr.flags);
+        ds_put_char(s, ',');
+
+        for (band_i = 0; band_i < omcr.n_bands; band_i++) {
+            ds_put_format(s, "band_type=%s,", ntohs(omcr.bands[band_i].type) == OFPMBT13_DROP ? "DROP" : "KNOWN");
+            ds_put_format(s, "rate=%"PRIu32",", ntohl(omcr.bands[band_i].rate));
+            ds_put_format(s, "burst=%"PRIu32"", ntohl(omcr.bands[band_i].burst_size));
+        }
+    }
+}
+
+static void
+ofp_print_meter_feature_reply(struct ds *s, const struct ofp13_meter_features *nmf)
+{
+    ds_put_char(s, '\n');
+
+    ds_put_format(s, "max_meter=%"PRIu32",", ntohl(nmf->max_meter));
+    ds_put_format(s, " band_types=%s\n", ntohl(nmf->band_types) == OFPMBT13_DROP ? "DROP" : "KNOWN");
+    ds_put_cstr(s,   "capabilities=");
+    ofp_print_meter_flags(s, ntohl(nmf->capabilities));
+
+    ds_put_char(s, '\n');
+    ds_put_format(s, "max_bands=%"PRIu8",", nmf->max_bands);
+    ds_put_format(s, " max_color=%"PRIu32"\n", nmf->max_color);
+}
+
 static void
 ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
                 struct ds *string, int verbosity)
@@ -1910,24 +2105,47 @@ 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);
         break;
 
+    /* Meter implementation */
+    case OFPTYPE_METER_MOD:
+        ofp_print_meter_mod(string, oh);
+        break;
+
+    case OFPTYPE_METER_CONFIG_REQUEST:
+        ofp_print_meter_config_request(string, oh);
+        break;
+
+    case OFPTYPE_METER_FEATURES_REQUEST:
+        ofp_print_stats_request(string, oh);
+        break;
+
+    case OFPTYPE_METER_REPLY:
+        ofp_print_stats_reply(string, oh);
+        ofp_print_meter_reply(string, oh);
+        break;
+
+    case OFPTYPE_METER_REQUEST:
+        ofp_print_stats_request(string, oh);
+        ofp_print_meter_request(string, oh);
+        break;
+
+    case OFPTYPE_METER_CONFIG_REPLY:
+        ofp_print_meter_config_reply(string, oh);
+        break;
+
+    case OFPTYPE_METER_FEATURES_REPLY:
+        ofp_print_meter_feature_reply(string, ofpmsg_body(oh));
+        break;
     case OFPTYPE_HELLO:
         ofp_print_hello(string, oh);
         break;
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 90f4f35..04d5ae2 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -5059,3 +5059,406 @@ ofputil_append_queue_stat(struct list *replies,
         NOT_REACHED();
     }
 }
+
+/* Meter */
+/* Find out usable protocols for meter */
+enum ofputil_protocol
+ofputil_meter_usable_protocols(void)
+{
+    enum ofputil_protocol usable_protocols;
+
+    usable_protocols = OFPUTIL_P_ANY;
+    usable_protocols &= OFPUTIL_P_OF13_OXM;
+
+    return usable_protocols;
+}
+
+/* Pull bands */
+static enum ofperr
+ofputil_pull_bands(struct ofpbuf *b, unsigned int bands_len,
+                     struct ofp13_meter_band_drop **bandsp, size_t *n_bandsp)
+{
+    if (bands_len % OFP_METER_BAND_ALIGN != 0) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bands length %u "
+                     "is not a multiple of %d", bands_len, OFP_METER_BAND_ALIGN);
+        goto error;
+    }
+
+    *bandsp = ofpbuf_try_pull(b, bands_len);
+    if (*bandsp == NULL) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bands length %u "
+                     "exceeds remaining message length (%zu)",
+                     bands_len, b->size);
+        goto error;
+    }
+
+    *n_bandsp = bands_len / OFP_METER_BAND_ALIGN;
+    return 0;
+
+error:
+    *bandsp = NULL;
+    *n_bandsp = 0;
+
+    return OFPERR_OFPBRC_BAD_LEN;
+}
+
+/* Converts 'mm' into an OFPT_METER_MOD message returns the message. */
+struct ofpbuf *
+ofputil_encode_meter_mod(const struct ofputil_meter_mod *mm,
+                    enum ofp_version ofp_version)
+{
+    struct ofpbuf *msg;
+    size_t band;
+
+    switch (ofp_version) {
+        case OFP13_VERSION: {
+            struct ofp13_meter_mod *omm;
+
+            msg = ofpraw_alloc(OFPRAW_OFPT13_METER_MOD, ofp_version, 0);
+            omm = ofpbuf_put_zeros(msg, sizeof *omm);
+            omm->command  = htons(mm->command);
+            omm->flags    = htons(mm->flags);
+            omm->meter_id = htonl(mm->meter_id);
+
+            for(band = 0; band < mm->n_bands; band ++)
+            {
+                ofpbuf_put(msg, (const void *)&(mm->bands[band]), sizeof(struct ofp13_meter_band_drop));
+            }
+            break;
+        }
+        case OFP10_VERSION:
+        case OFP11_VERSION:
+        case OFP12_VERSION:
+            return NULL;
+        default:
+            NOT_REACHED();
+    }
+
+    ofpmsg_update_length(msg);
+
+    return msg;
+}
+
+/* Decode meter mod message */
+enum ofperr
+ofputil_decode_meter_mod(struct ofputil_meter_mod *mm,
+                        const struct ofp_header *oh)
+{
+    struct ofpbuf b;
+    enum ofpraw raw;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+    if (raw == OFPRAW_OFPT13_METER_MOD) {
+        const struct ofp13_meter_mod *omm;
+        enum ofperr error;
+
+        omm = ofpbuf_pull(&b, sizeof *omm);
+
+        if (ntohs(omm->command) != OFPMC13_DELETE) {
+            if (!b.size) {
+                VLOG_WARN_RL(&bad_ofmsg_rl, "Invalid meter id: %"PRIu32", need specify a band",
+                    ntohl(omm->meter_id));
+                return OFPERR_OFPMMFC_INVALID_METER;
+            }
+
+            error  = ofputil_pull_bands(&b, b.size, &mm->bands, &mm->n_bands);
+            if (error) {
+                return error;
+            }
+
+            if (mm->n_bands > OFP_MAX_BAND_PER_METER) {
+                VLOG_WARN_RL(&bad_ofmsg_rl, "Invalid meter, id : %"PRIu32". Only %"PRIu32" bands per meter are supported.",
+                    ntohl(omm->meter_id),
+                    OFP_MAX_BAND_PER_METER);
+                return OFPERR_OFPMMFC_OUT_OF_BANDS;
+            }
+        }
+        if (b.size) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "METER_MOD request claims invalid "
+                         "length %zu", b.size);
+            return OFPERR_OFPMMFC_INVALID_METER;
+        }
+
+        mm->meter_id = ntohl(omm->meter_id);
+        mm->flags    = ntohs(omm->flags);
+        mm->command  = ntohs(omm->command);
+
+    } else {
+        NOT_REACHED();
+    }
+
+    return 0;
+}
+
+/* Decode meter reply message */
+enum ofperr
+ofputil_decode_meter_reply(struct ofputil_meter_stats_reply *omsr,
+                                struct ofpbuf *msg)
+{
+    enum ofperr error;
+    enum ofpraw raw;
+
+    error = (msg->l2
+             ? ofpraw_decode(&raw, msg->l2)
+             : ofpraw_pull(&raw, msg));
+    if (error) {
+        return error;
+    }
+
+    if (!msg->size) {
+        return EOF;
+    } else if (raw == OFPRAW_OFPST13_METER_REPLY) {
+        const struct ofp13_meter_stats *oms13;
+        size_t length;
+
+        oms13 = ofpbuf_try_pull(msg, sizeof *oms13);
+        if (!oms13) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_METER reply has %zu leftover "
+                         "bytes at end", msg->size);
+            return EINVAL;
+        }
+
+        length = ntohs(oms13->len);
+        if (length < sizeof *oms13) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_METER reply claims invalid "
+                         "length %zu", length);
+            return EINVAL;
+        }
+
+        omsr->meter_id        = ntohl(oms13->meter_id);
+        omsr->len             = ntohs(oms13->len);
+        omsr->flow_count      = ntohl(oms13->flow_count);
+        omsr->packet_in_count = ntohll(oms13->packet_in_count);
+        omsr->byte_in_count   = ntohll(oms13->byte_in_count);
+        omsr->duration_sec    = ntohl(oms13->duration_sec);
+        omsr->duration_nsec   = ntohl(oms13->duration_nsec);
+
+        omsr->n_bands = (omsr->len - sizeof *oms13) / sizeof(struct ofp13_meter_band_stats);
+
+        omsr->bands_stats = ofpbuf_try_pull(msg, omsr->n_bands * sizeof(struct ofp13_meter_band_stats));
+
+        if (!omsr->bands_stats) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_METER reply has %zu leftover "
+                     "bytes at end", msg->size);
+            return EINVAL;
+        }
+    }
+    else {
+        NOT_REACHED();
+    }
+
+    return 0;
+}
+
+/* Decode meter config reply message */
+enum ofperr
+ofputil_decode_meter_config_reply(struct ofputil_meter_config_reply *omcr,
+                                struct ofpbuf *msg)
+{
+    enum ofperr error;
+    enum ofpraw raw;
+
+    error = (msg->l2
+             ? ofpraw_decode(&raw, msg->l2)
+             : ofpraw_pull(&raw, msg));
+    if (error) {
+        return error;
+    }
+
+    if (!msg->size) {
+        return EOF;
+    } else if (raw == OFPRAW_OFPST13_METER_CONFIG_REPLY) {
+        const struct ofp13_meter_config *omc13;
+        size_t length;
+
+        omc13 = ofpbuf_try_pull(msg, sizeof *omc13);
+        if (!omc13) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_METER_CONFIG reply has %zu leftover "
+                         "bytes at end", msg->size);
+            return EINVAL;
+        }
+
+        length = ntohs(omc13->length);
+        if (length < sizeof *omc13) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_METER_CONFIG reply claims invalid "
+                         "length %zu", length);
+            return EINVAL;
+        }
+
+        omcr->flags = ntohs(omc13->flags);
+        omcr->length = ntohs(omc13->length);
+        omcr->meter_id = ntohl(omc13->meter_id);
+        omcr->n_bands = (omcr->length - sizeof *omc13) / sizeof(struct ofp13_meter_band_drop);
+        omcr->bands = ofpbuf_try_pull(msg, omcr->n_bands * sizeof(struct ofp13_meter_band_drop));
+
+        if (!omcr->bands) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFST_METER_CONFIG reply has %zu leftover "
+                    "bytes at end", msg->size);
+            return EINVAL;
+        }
+    }
+    else {
+        NOT_REACHED();
+    }
+
+    return 0;
+}
+
+/* Encode meter config request message */
+struct ofpbuf *
+ofputil_encode_meter_config_request(const struct ofputil_meter_request *msr, enum ofp_version ofp_version)
+{
+        struct ofpbuf *request = NULL;
+
+        switch (ofp_version) {
+        case OFP10_VERSION:
+        case OFP11_VERSION:
+        case OFP12_VERSION: {
+            return NULL;
+        }
+        case OFP13_VERSION: {
+            struct ofp13_meter_multipart_request *ommr;
+            request = ofpraw_alloc(OFPRAW_OFPST13_METER_CONFIG_REQUEST, ofp_version, 0);
+            ommr = ofpbuf_put_zeros(request, sizeof *ommr);
+            ommr->meter_id = htonl(msr->meter_id);
+            break;
+        }
+        default:
+            NOT_REACHED();
+        }
+
+        ofpmsg_update_length(request);
+
+        return request;
+}
+
+/* Encode meter features request message */
+struct ofpbuf *
+ofputil_encode_meter_features_request(enum ofp_version ofp_version)
+{
+        struct ofpbuf *request = NULL;
+
+        switch (ofp_version) {
+        case OFP10_VERSION:
+        case OFP11_VERSION:
+        case OFP12_VERSION: {
+            return NULL;
+        }
+        case OFP13_VERSION: {
+            request = ofpraw_alloc(OFPRAW_OFPST13_METER_FEATURES_REQUEST, ofp_version, 0);
+            break;
+        }
+        default:
+            NOT_REACHED();
+        }
+
+        return request;
+}
+
+/* Encode meter stats request message */
+struct ofpbuf *
+ofputil_encode_meter_stats_request(const struct ofputil_meter_request *msr, enum ofp_version ofp_version)
+{
+    struct ofpbuf *request = NULL;
+
+    switch (ofp_version) {
+    case OFP10_VERSION:
+    case OFP11_VERSION:
+    case OFP12_VERSION: {
+        return NULL;
+    }
+    case OFP13_VERSION: {
+        struct ofp13_meter_multipart_request *req;
+        request = ofpraw_alloc(OFPRAW_OFPST13_METER_REQUEST, ofp_version, 0);
+        req = ofpbuf_put_zeros(request, sizeof *req);
+        req->meter_id = htonl(msr->meter_id);
+        break;
+    }
+    default:
+        NOT_REACHED();
+    }
+    ofpmsg_update_length(request);
+
+    return request;
+}
+
+/* Decode meter request message */
+enum ofperr
+ofputil_decode_meter_request(struct ofputil_meter_request *msr,
+                                 const struct ofp_header *oh)
+{
+    struct ofpbuf b;
+    enum ofpraw raw;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+    if (raw == OFPRAW_OFPST13_METER_REQUEST ||
+        raw == OFPRAW_OFPST13_METER_CONFIG_REQUEST) {
+        const struct ofp13_meter_multipart_request *ommr;
+
+        ommr = ofpbuf_pull(&b, sizeof *ommr);
+        msr->meter_id = ntohl(ommr->meter_id);
+    } else {
+        return OFPERR_OFPBRC_BAD_TYPE;
+    }
+
+    return 0;
+}
+
+/* Append meter stats reply message */
+void
+ofputil_append_meter_stats_reply(const struct ofputil_meter_stats_reply *msr,
+                                struct list *replies)
+{
+    struct ofp13_meter_stats *oms;
+
+    oms = ofpmp_append(replies, sizeof *oms);
+    oms->meter_id = htonl(msr->meter_id);
+    memset(oms->pad, 0, sizeof(oms->pad));
+    oms->flow_count      = htonl(msr->flow_count);
+    oms->duration_sec    = htonl(msr->duration_sec);
+    oms->duration_nsec   = htonl(msr->duration_nsec);
+    oms->packet_in_count = htonll(msr->packet_in_count);
+    oms->byte_in_count   = htonll(msr->byte_in_count);
+    oms->len             = htons(msr->len);
+}
+
+/* Append meter band stats reply message */
+void
+ofputil_append_meter_band_stats_reply(const struct ofputil_meter_band_stats_reply *mbsr,
+                                struct list *replies)
+{
+    struct ofp13_meter_band_stats *nmbs;
+
+    nmbs = ofpmp_append(replies, sizeof *nmbs);
+    nmbs->packet_band_count = htonll(mbsr->packet_band_count);
+    nmbs->byte_band_count   = htonll(mbsr->byte_band_count);
+}
+
+/* Append meter config reply message */
+void
+ofputil_append_meter_config_reply(const struct ofputil_meter_config_reply *mcr,
+                                struct list *replies)
+{
+    struct ofp13_meter_config *mc;
+
+    mc = ofpmp_append(replies, sizeof *mc);
+    mc->length   = htons(mcr->length);
+    mc->flags    = htons(mcr->flags);
+    mc->meter_id = htonl(mcr->meter_id);
+}
+
+/* Append meter band config reply message */
+void
+ofputil_append_meter_band_config_reply(const struct ofputil_meter_band_config_reply *mbc,
+                                struct list *replies)
+{
+    struct ofp13_meter_band_drop *nmb;
+
+    nmb = ofpmp_append(replies, sizeof *nmb);
+    nmb->type       = htons(mbc->type);
+    nmb->len        = htons(mbc->len);
+    nmb->rate       = htonl(mbc->rate);
+    nmb->burst_size = htonl(mbc->burst_size);
+}
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index f8705a2..bab1d02 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -729,4 +729,111 @@ int ofputil_decode_queue_stats(struct ofputil_queue_stats *qs, struct ofpbuf *ms
 void ofputil_append_queue_stat(struct list *replies,
                                const struct ofputil_queue_stats *oqs);
 
+/* Meter modify message */
+#define OFP_METER_BAND_ALIGN 16      /* Alignment of ofp_actions. */
+#define OFP_MAX_BAND_PER_METER  1
+
+struct ofputil_meter_stats {
+    uint64_t packet_in_count;   /* Number of packets in input. */
+    uint64_t byte_in_count;     /* Number of bytes in input. */
+
+    struct ofp13_meter_band_stats bands_stats[OFP_MAX_BAND_PER_METER];
+    size_t n_bands;
+};
+
+struct ofputil_meter_mod {
+    uint16_t command;         /* One of OFPMC_*. */
+    uint16_t flags;           /* One of OFPMF_*. */
+    uint32_t meter_id;        /* Meter instance. */
+    struct ofp13_meter_band_drop *bands; /*TODO Check band type */
+    size_t n_bands;
+};
+
+struct ofputil_meter_request {
+    uint32_t meter_id;
+    uint8_t pad[4];
+};
+
+struct ofputil_meter_band_stats_reply {
+    uint64_t packet_band_count;   /* Number of packets in band. */
+    uint64_t byte_band_count;     /* Number of bytes in band. */
+};
+
+struct ofputil_meter_stats_reply {
+    uint32_t meter_id;          /* Meter instance. */
+    uint16_t len;               /* Length in bytes of this stats. */
+    uint8_t  pad[6];            /* Padding */
+    uint32_t flow_count;        /* Number of flows bound to meter. */
+    uint64_t packet_in_count;   /* Number of packets in input. */
+    uint64_t byte_in_count;     /* Number of bytes in input. */
+    uint32_t duration_sec;      /* Time meter has been alive in seconds. */
+    uint32_t duration_nsec;     /* Time meter has been alive in nanoseconds beyond duration_sec. */
+
+    struct ofp13_meter_band_stats* bands_stats;
+    uint16_t n_bands;
+    uint16_t resv;
+};
+
+struct ofputil_meter_band_config_reply {
+    uint16_t type;
+    uint16_t len;
+    uint32_t rate;
+    uint32_t burst_size;
+    uint32_t resved;
+};
+
+struct ofputil_meter_config_reply {
+    uint16_t length;
+    uint16_t flags;
+    uint32_t meter_id;
+
+    struct ofp13_meter_band_drop* bands;
+    uint16_t n_bands;
+    uint16_t resv;
+};
+
+struct ofputil_meter_features_reply {
+    uint32_t max_meter;         /* Maximum number of meters. */
+    uint32_t band_types;        /* Bitmaps of OFPMBT_* values supported. */
+    uint32_t capabilities;      /* Bitmaps of "nx_meter_flags". */
+    uint8_t max_bands;          /* Maximum bands per meters */
+    uint8_t max_color;          /* Maximum color value */
+};
+
+/* Meter */
+enum ofputil_protocol
+ofputil_meter_usable_protocols(void);
+struct ofpbuf *
+ofputil_encode_meter_mod(const struct ofputil_meter_mod *mm, enum ofp_version ofp_version);
+struct ofpbuf *
+ofputil_encode_meter_stats_request(const struct ofputil_meter_request *msr, enum ofp_version ofp_version);
+struct ofpbuf *
+ofputil_encode_meter_config_request(const struct ofputil_meter_request *msr, enum ofp_version ofp_version);
+struct ofpbuf *
+ofputil_encode_meter_features_request(enum ofp_version ofp_version);
+enum ofperr
+ofputil_decode_meter_mod(struct ofputil_meter_mod *mm,
+                                const struct ofp_header *oh);
+enum ofperr
+ofputil_decode_meter_request(struct ofputil_meter_request *msr,
+                                 const struct ofp_header *oh);
+enum ofperr
+ofputil_decode_meter_reply(struct ofputil_meter_stats_reply *omsr,
+                                struct ofpbuf *msg);
+enum ofperr
+ofputil_decode_meter_config_reply(struct ofputil_meter_config_reply *omcr,
+                                struct ofpbuf *msg);
+void
+ofputil_append_meter_stats_reply(const struct ofputil_meter_stats_reply *msr,
+                                struct list *replies);
+void
+ofputil_append_meter_band_stats_reply(const struct ofputil_meter_band_stats_reply *mbsr,
+                                struct list *replies);
+void
+ofputil_append_meter_config_reply(const struct ofputil_meter_config_reply *mcr,
+                                struct list *replies);
+void
+ofputil_append_meter_band_config_reply(const struct ofputil_meter_band_config_reply *mbc,
+                                struct list *replies);
+
 #endif /* ofp-util.h */
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index a52a8cf..dd27e9f 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1772,6 +1772,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
         }
 
+        case OFPACT_METER:
+            NOT_REACHED();
+            break;
+
         case OFPACT_SAMPLE:
             xlate_sample_action(ctx, ofpact_get_SAMPLE(a));
             break;
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 6fa7894..09060c8 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -6772,4 +6772,13 @@ const struct ofproto_class ofproto_dpif_class = {
     forward_bpdu_changed,
     set_mac_table_config,
     set_realdev,
+
+    NULL,                       /* meter_alloc */
+    NULL,                       /* meter_construct */
+    NULL,                       /* meter_destruct */
+    NULL,                       /* meter_dealloc */
+    NULL,                       /* meter_get_stats */
+    NULL,                       /* meter_update */
+    NULL,                       /* meter_get_refcnt */
+    NULL,                       /* meter_get_features */
 };
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 565cb01..da9e0c5 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -24,6 +24,7 @@
 #include "classifier.h"
 #include "heap.h"
 #include "hindex.h"
+#include "ihash.h"
 #include "list.h"
 #include "ofp-errors.h"
 #include "ofp-util.h"
@@ -100,6 +101,8 @@ struct ofproto {
     unsigned long int *vlan_bitmap; /* 4096-bit bitmap of in-use VLANs. */
     bool vlans_changed;             /* True if new VLANs are in use. */
     int min_mtu;                    /* Current MTU of non-internal ports. */
+
+    struct ihash meters;          /* hash struct ofmeter indexed by meter-id */
 };
 
 void ofproto_init_tables(struct ofproto *, int n_tables);
@@ -257,6 +260,20 @@ bool ofoperation_has_out_port(const struct ofoperation *, ofp_port_t out_port);
 
 bool ofproto_rule_is_hidden(const struct rule *);
 
+struct ofmeter {
+    struct ofproto *ofproto;     /* The ofproto that contains this meter. */
+
+    long long int created;       /* Creation time. */
+    long long int modified;      /* Time of last modification. */
+
+    uint32_t meter_id;
+
+    struct ofp13_meter_band_drop bands[OFP_MAX_BAND_PER_METER];
+    uint16_t n_bands;
+
+    uint16_t flags;              /* Bitmap of NXMF_* */
+};
+
 /* ofproto class structure, to be defined by each ofproto implementation.
  *
  *
@@ -1321,6 +1338,16 @@ struct ofproto_class {
      * it. */
     int (*set_realdev)(struct ofport *ofport,
                        ofp_port_t realdev_ofp_port, int vid);
+
+    struct ofmeter *(*meter_alloc)(void);
+    int (*meter_construct)(struct ofmeter *meter);
+    int (*meter_destruct)(struct ofmeter *meter);
+    void (*meter_dealloc)(struct ofmeter *meter);
+    int (*meter_get_stats)(struct ofmeter *meter,
+                        struct ofputil_meter_stats* meter_stats);
+    int (*meter_update)(struct ofmeter *meter);
+    int (*meter_get_refcnt)(struct ofmeter *meter, uint32_t* refcnt);
+    void (*meter_get_features)(struct ofproto *ofproto, uint32_t* max_meter, uint32_t* min_meter, uint8_t* max_bands);
 };
 
 extern const struct ofproto_class ofproto_dpif_class;
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index afd8e17..b67d7e2 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -438,6 +438,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     ofproto->vlan_bitmap = NULL;
     ofproto->vlans_changed = false;
     ofproto->min_mtu = INT_MAX;
+    ihash_init(&ofproto->meters);
 
     error = ofproto->ofproto_class->construct(ofproto);
     if (error) {
@@ -1089,6 +1090,8 @@ ofproto_destroy__(struct ofproto *ofproto)
 
     free(ofproto->vlan_bitmap);
 
+    ihash_destroy_free_data(&ofproto->meters);
+
     ofproto->ofproto_class->dealloc(ofproto);
 }
 
@@ -4106,6 +4109,525 @@ handle_flow_monitor_cancel(struct ofconn *ofconn, const struct ofp_header *oh)
     return 0;
 }
 
+static bool
+rule_has_meter_id(const struct rule *p_rule, uint32_t meter_id)
+{
+    const struct ofpact *a;
+    const struct ofpact *ofpacts = p_rule->ofpacts;
+    int ofpacts_len = p_rule->ofpacts_len;
+
+    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+        struct ofpact_meter * meter;
+
+        if (OFPACT_METER == a->type) {
+            meter = ofpact_get_METER(a);
+            if (meter->meter_id == meter_id) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+static enum ofperr
+collect_rules_loose_meter(struct ofproto *ofproto, uint8_t table_id,
+                    const struct match *match, uint32_t meter_id, struct list *rules)
+{
+    struct oftable *table;
+    struct cls_rule cr;
+    enum ofperr error = 0;
+    ovs_be64 cookie = 0;
+    ovs_be64 cookie_mask = 0;
+
+    error = check_table_id(ofproto, table_id);
+    if (error) {
+        return error;
+    }
+
+    list_init(rules);
+    cls_rule_init(&cr, match, 0);
+
+    OFPROTO_FOR_EACH_TABLE (table, ofproto) {
+        struct cls_cursor cursor;
+        struct rule *rule;
+
+        cls_cursor_init(&cursor, &table->cls, NULL);
+        CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
+            if (rule->pending) {
+                error = OFPROTO_POSTPONE;
+                goto exit;
+            }
+            if (!ofproto_rule_is_hidden(rule)
+                    && rule_has_meter_id(rule, meter_id)
+                    && !((rule->flow_cookie ^ cookie) & cookie_mask)) {
+                list_push_back(rules, &rule->ofproto_node);
+            }
+        }
+    }
+
+exit:
+    cls_rule_destroy(&cr);
+    return error;
+}
+
+static void
+ofproto_rule_expire_due_to_meter_del(struct rule *rule, uint8_t reason)
+{
+    struct ofproto *ofproto = rule->ofproto;
+    struct ofopgroup *group;
+
+    ovs_assert(reason == OFPRR_DELETE);
+
+    ofproto_rule_send_removed(rule, reason);
+
+    group = ofopgroup_create_unattached(ofproto);
+    ofoperation_create(group, rule, OFOPERATION_DELETE, reason);
+    oftable_remove_rule(rule);
+    rule->ofproto->ofproto_class->rule_destruct(rule);
+    ofopgroup_submit(group);
+}
+
+static int
+meter_del_remove_rules(struct ofproto *ofproto, uint32_t meter_id)
+{
+    struct list rules;
+    struct match match;
+    struct rule *rule, *next;
+    enum ofperr error;
+
+    list_init(&rules);
+    match_init_catchall(&match);
+    error = collect_rules_loose_meter(ofproto, 0, &match, meter_id, &rules);
+    if (error || list_is_empty(&rules))
+    {
+        return error;
+    }
+
+    LIST_FOR_EACH_SAFE (rule, next, ofproto_node, &rules) {
+        ofproto_rule_expire_due_to_meter_del(rule, OFPRR_DELETE);
+    }
+
+    return 0;
+}
+
+static enum ofperr
+add_meter(struct ofproto *ofproto, struct ofconn *ofconn,
+         const struct ofputil_meter_mod *mm, const struct ofp_header *request)
+{
+    int error;
+    struct ofmeter* new_meter = NULL;
+    struct ihash_node *ihash_node = NULL;
+
+    ofconn = ofconn; /* TODO unused parameter */
+    request = request; /* TODO unused parameter */
+
+    if (mm->meter_id < 1 || mm->meter_id > OFPM13_MAX) {
+        VLOG_ERR("Fail to add meter, invalid meter_id %"PRIu32"\n", mm->meter_id);
+        return OFPERR_OFPMMFC_INVALID_METER;
+    }
+
+    ihash_node = ihash_find(&ofproto->meters, mm->meter_id);
+
+    if (ihash_node) {
+        VLOG_ERR("Fail to add meter, meter_id %"PRIu32" is existed\n", mm->meter_id);
+        return OFPERR_OFPMMFC_METER_EXISTS;
+    }
+
+    new_meter = malloc(sizeof(struct ofmeter));
+    memset(new_meter, 0, sizeof(struct ofmeter));
+    new_meter->meter_id = mm->meter_id;
+    new_meter->flags    = mm->flags;
+    new_meter->n_bands  = mm->n_bands;
+    memcpy(new_meter->bands, mm->bands, sizeof(struct ofp13_meter_band_drop) * mm->n_bands);
+
+    new_meter->ofproto = ofproto;
+    new_meter->created = new_meter->modified = time_msec();
+
+    error = ofproto->ofproto_class->meter_construct(new_meter);
+
+    if (error) {
+        free(new_meter);
+        return error;
+    }
+
+    ihash_add(&ofproto->meters, new_meter->meter_id, new_meter);
+
+    return 0;
+}
+
+
+static enum ofperr
+modify_meter(struct ofproto *ofproto, struct ofconn *ofconn,
+         const struct ofputil_meter_mod *mm, const struct ofp_header *request)
+{
+    int error;
+    struct ofmeter* old_meter = NULL;
+    struct ofmeter* new_meter = NULL;
+    struct ihash_node *ihash_node = NULL;
+
+    ofconn = ofconn; /* TODO unused parameter */
+    request = request; /* TODO unused parameter */
+
+    if (mm->meter_id < 1 || mm->meter_id > OFPM13_MAX) {
+        VLOG_ERR("Fail to modify meter, invalid meter_id %"PRIu32"\n", mm->meter_id);
+        return OFPERR_OFPMMFC_INVALID_METER;
+    }
+
+    ihash_node = ihash_find(&ofproto->meters, mm->meter_id);
+
+    if (!ihash_node) {
+        VLOG_ERR("Fail to modify meter, meter_id %"PRIu32" is existed\n", mm->meter_id);
+        return OFPERR_OFPMMFC_UNKNOWN_METER;
+    }
+
+    old_meter = (struct ofmeter*) ihash_node->data;
+
+    new_meter = malloc(sizeof(struct ofmeter));
+    memset(new_meter, 0, sizeof(struct ofmeter));
+    new_meter->meter_id = mm->meter_id;
+    new_meter->flags    = mm->flags;
+    new_meter->n_bands  = mm->n_bands;
+    memcpy(new_meter->bands, mm->bands, sizeof(struct ofp13_meter_band_drop) * mm->n_bands);
+
+    new_meter->ofproto  = ofproto;
+    new_meter->created  = old_meter->created;
+    new_meter->modified = time_msec();
+
+    error = ofproto->ofproto_class->meter_update(new_meter);
+    if (error) {
+        free(new_meter);
+        return error;
+    }
+
+    old_meter->modified = new_meter->modified;
+    old_meter->flags    = new_meter->flags;
+    old_meter->n_bands  = new_meter->n_bands;
+    memcpy(old_meter->bands, new_meter->bands, sizeof(new_meter->bands));
+    free(new_meter);
+
+    return 0;
+}
+
+static enum ofperr
+delete_meter(struct ofproto *ofproto, struct ofconn *ofconn,
+                   struct ofputil_meter_mod *mm,
+                   const struct ofp_header *request)
+{
+    struct ofmeter* meter = NULL;
+    int error;
+    struct ihash_node *ihash_node = NULL;
+
+    if (mm->meter_id == OFPM13_ALL) {
+        struct ihash_node *node, *next;
+
+        IHASH_FOR_EACH_SAFE (node, next, &ofproto->meters) {
+            meter = (struct ofmeter*) node->data;
+            mm->meter_id = meter->meter_id;
+            delete_meter(ofproto, ofconn, mm, request);
+        }
+
+        return 0;
+    }
+
+    if (mm->meter_id < 1 || mm->meter_id > OFPM13_MAX) {
+        VLOG_ERR("Fail to modify meter, invalid meter_id %"PRIu32"\n", mm->meter_id);
+        return OFPERR_OFPMMFC_INVALID_METER;
+    }
+
+    ihash_node = ihash_find(&ofproto->meters, mm->meter_id);
+
+    if (!ihash_node) {
+        return 0;
+    }
+
+    meter = (struct ofmeter*) ihash_node->data;
+
+    meter_del_remove_rules(ofproto, meter->meter_id);
+
+    error = ofproto->ofproto_class->meter_destruct(meter);
+    if (error) {
+        return error;
+    }
+
+    ihash_delete(&ofproto->meters, ihash_node);
+    free(meter);
+
+    return 0;
+}
+
+static enum ofperr
+handle_meter_mod__(struct ofproto *ofproto, struct ofconn *ofconn,
+                  const struct ofputil_meter_mod *mm,
+                  const struct ofp_header *oh)
+{
+    switch (mm->command) {
+    case OFPMC13_ADD:
+        return add_meter(ofproto, ofconn, mm, oh);
+
+    case OFPMC13_MODIFY:
+        return modify_meter(ofproto, ofconn, mm, oh);
+
+    case OFPMC13_DELETE:
+    {
+        struct ofputil_meter_mod omm;
+        memset(&omm, 0, sizeof(omm));
+        omm.meter_id = mm->meter_id;
+        return delete_meter(ofproto, ofconn, &omm, oh);
+    }
+    default:
+        return OFPERR_OFPMMFC_BAD_COMMAND;
+    }
+}
+
+static enum ofperr
+handle_meter_mod(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    struct ofputil_meter_mod mm;
+    enum ofperr error;
+
+    error = reject_slave_controller(ofconn);
+    if (error) {
+        return error;
+    }
+
+    memset(&mm, 0, sizeof(mm));
+    error = ofputil_decode_meter_mod(&mm, oh);
+    if (error) {
+        return error;
+    }
+
+    return handle_meter_mod__(ofconn_get_ofproto(ofconn), ofconn, &mm, oh);
+}
+
+static enum ofperr
+handle_meter_request__(struct ofproto *ofproto, const struct ofp_header *request,
+                          struct ofmeter* meter, struct list* replies)
+{
+    struct ofputil_meter_stats meter_stats;
+    struct ofputil_meter_stats_reply ms;
+    size_t band_i = 0;
+    uint32_t refcnt = 0;
+
+    request = request; /* TODO unused parameter */
+
+    memset(&meter_stats, 0, sizeof(meter_stats));
+    ofproto->ofproto_class->meter_get_stats(meter, &meter_stats);
+    ofproto->ofproto_class->meter_get_refcnt(meter, &refcnt);
+
+    memset(&ms, 0, sizeof(ms));
+    ms.meter_id   = meter->meter_id;
+    ms.len        = sizeof(struct ofp13_meter_stats) + sizeof(struct ofp13_meter_band_stats) * meter->n_bands;
+    ms.flow_count = refcnt;
+    ms.packet_in_count = meter_stats.packet_in_count;
+    ms.byte_in_count   = meter_stats.byte_in_count;
+    ms.n_bands         = meter->n_bands;
+    calc_duration(meter->created, time_msec(),
+                  &ms.duration_sec, &ms.duration_nsec);
+    ofputil_append_meter_stats_reply(&ms, replies);
+
+    for (band_i = 0; band_i < meter->n_bands; band_i++){
+        struct ofputil_meter_band_stats_reply mbs;
+
+        mbs.packet_band_count = ntohll(meter_stats.bands_stats[band_i].packet_band_count);
+        mbs.byte_band_count   = ntohll(meter_stats.bands_stats[band_i].byte_band_count);
+
+        ofputil_append_meter_band_stats_reply(&mbs, replies);
+    }
+
+    return 0;
+}
+
+static enum ofperr
+handle_meter_request(struct ofconn *ofconn, const struct ofp_header *request)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    struct ofputil_meter_request msr;
+    struct list replies;
+    struct ihash_node *ihash_node = NULL;
+    struct ofmeter* meter = NULL;
+    enum ofp_version version;
+    enum ofperr error;
+    enum ofpraw raw;
+    uint32_t max_meter;
+    uint32_t min_meter;
+    uint8_t max_bands;
+
+    version = ofputil_protocol_to_ofp_version(ofconn_get_protocol(ofconn));
+    switch (version) {
+    case OFP13_VERSION:
+        raw = OFPRAW_OFPST13_METER_REPLY;
+        break;
+    case OFP10_VERSION:
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+    default:
+        NOT_REACHED();
+    }
+
+    error = ofputil_decode_meter_request(&msr, request);
+    if (error) {
+        return error;
+    }
+    ofproto->ofproto_class->meter_get_features(ofproto, &max_meter, &min_meter, &max_bands);
+
+    ofpmp_init(&replies, request);
+    if (0 < msr.meter_id && msr.meter_id <= OFPM13_MAX) {
+        ihash_node = ihash_find(&ofproto->meters, msr.meter_id);
+
+        if (ihash_node) {
+            meter = (struct ofmeter*) ihash_node->data;
+            handle_meter_request__(ofproto, request, meter, &replies);
+        }
+
+    } else if (msr.meter_id == OFPM13_ALL){
+        uint32_t meter_id;
+
+        for (meter_id = 1; meter_id <= max_meter; meter_id++) {
+            ihash_node = ihash_find(&ofproto->meters, meter_id);
+
+            if (!ihash_node) {
+                continue;
+            }
+            meter = (struct ofmeter*) ihash_node->data;
+            handle_meter_request__(ofproto, request, meter, &replies);
+        }
+    }
+
+    ofconn_send_replies(ofconn, &replies);
+
+    return 0;
+}
+
+static enum ofperr
+handle_meter_config_request__(struct ofproto *ofproto, const struct ofp_header *request,
+                          struct ofmeter* meter, struct list* replies)
+{
+    struct ofputil_meter_config_reply mcr;
+    size_t band_i = 0;
+
+    ofproto = ofproto; /* TODO unused parameter */
+    request = request; /* TODO unused parameter */
+
+    mcr.flags    = meter->flags;
+    mcr.meter_id = meter->meter_id;
+    mcr.length   = sizeof(struct ofp13_meter_config) + meter->n_bands * sizeof(struct ofp13_meter_band_drop);
+    mcr.n_bands  = meter->n_bands;
+    mcr.bands    = meter->bands;
+
+    ofputil_append_meter_config_reply(&mcr, replies);
+
+    for (band_i = 0; band_i < meter->n_bands; band_i++){
+        struct ofputil_meter_band_config_reply mbc;
+        mbc.type       = ntohs(meter->bands[band_i].type);
+        mbc.len        = sizeof(struct ofp13_meter_band_drop);
+        mbc.rate       = ntohl(meter->bands[band_i].rate);
+        mbc.burst_size = ntohl(meter->bands[band_i].burst_size);
+
+        ofputil_append_meter_band_config_reply(&mbc, replies);
+    }
+
+    return 0;
+}
+
+static enum ofperr
+handle_meter_config_request(struct ofconn *ofconn, const struct ofp_header *request)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    struct ofputil_meter_request msr;
+    enum ofp_version version;
+    struct list replies;
+    struct ihash_node *ihash_node = NULL;
+    struct ofmeter* meter = NULL;
+    enum ofperr error;
+    enum ofpraw raw;
+    uint32_t max_meter;
+    uint32_t min_meter;
+    uint8_t max_bands;
+
+    version = ofputil_protocol_to_ofp_version(ofconn_get_protocol(ofconn));
+    switch (version) {
+    case OFP13_VERSION:
+        raw = OFPRAW_OFPST13_METER_CONFIG_REPLY;
+        break;
+    case OFP10_VERSION:
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+    default:
+        NOT_REACHED();
+    }
+
+    error = ofputil_decode_meter_request(&msr, request);
+    if (error) {
+        return error;
+    }
+    ofproto->ofproto_class->meter_get_features(ofproto, &max_meter, &min_meter, &max_bands);
+
+    ofpmp_init(&replies, request);
+    if (0 < msr.meter_id && msr.meter_id < OFPM13_MAX) {
+        ihash_node = ihash_find(&ofproto->meters, msr.meter_id);
+
+        if (ihash_node) {
+            meter = (struct ofmeter*) ihash_node->data;
+
+            handle_meter_config_request__(ofproto, request, meter, &replies);
+        }
+    } else if (msr.meter_id == OFPM13_ALL) {
+        uint32_t meter_id;
+        for (meter_id = min_meter; meter_id <= max_meter; meter_id++) {
+            ihash_node = ihash_find(&ofproto->meters, meter_id);
+
+            if (!ihash_node) {
+                continue;
+            }
+
+            meter = (struct ofmeter*) ihash_node->data;
+            handle_meter_config_request__(ofproto, request, meter, &replies);
+        }
+    }
+
+    ofconn_send_replies(ofconn, &replies);
+
+    return 0;
+}
+
+static enum ofperr
+handle_meter_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    struct ofpbuf *b;
+    struct ofp13_meter_features *omf;
+    enum ofp_version version;
+    uint32_t max_meter;
+    uint32_t min_meter;
+    uint8_t max_bands;
+    enum ofpraw raw;
+
+    version = ofputil_protocol_to_ofp_version(ofconn_get_protocol(ofconn));
+    switch (version) {
+    case OFP13_VERSION:
+        raw = OFPRAW_OFPST13_METER_FEATURES_REPLY;
+        break;
+    case OFP10_VERSION:
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+    default:
+        NOT_REACHED();
+    }
+
+    ofproto->ofproto_class->meter_get_features(ofproto, &max_meter, &min_meter, &max_bands);
+    b = ofpraw_alloc_xid(raw, version, oh->xid, 0);
+    omf = ofpbuf_put_zeros(b, sizeof *omf);
+    omf->max_meter    = htonl(max_meter);
+    omf->band_types   = htonl(OFPMBT13_DROP);
+    omf->capabilities = htonl(OFPMF13_KBPS | OFPMF13_BURST | OFPMF13_STATS);
+    omf->max_bands    = max_bands;
+    omf->max_color    = 0;
+
+    ofconn_send_reply(ofconn, b);
+    return 0;
+}
+
 static enum ofperr
 handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
 {
@@ -4199,16 +4721,25 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
         return handle_flow_monitor_request(ofconn, oh);
 
+        /* Meters. */
+    case OFPTYPE_METER_MOD:
+        return handle_meter_mod(ofconn, oh);
+
+    case OFPTYPE_METER_REQUEST:
+        return handle_meter_request(ofconn, oh);
+
+    case OFPTYPE_METER_CONFIG_REQUEST:
+        return handle_meter_config_request(ofconn, oh);
+
+    case OFPTYPE_METER_FEATURES_REQUEST:
+        return handle_meter_features_request(ofconn, oh);
+
         /* FIXME: Change the following once they are implemented: */
     case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
     case OFPTYPE_GET_ASYNC_REQUEST:
-    case OFPTYPE_METER_MOD:
     case OFPTYPE_GROUP_REQUEST:
     case OFPTYPE_GROUP_DESC_REQUEST:
     case OFPTYPE_GROUP_FEATURES_REQUEST:
-    case OFPTYPE_METER_REQUEST:
-    case OFPTYPE_METER_CONFIG_REQUEST:
-    case OFPTYPE_METER_FEATURES_REQUEST:
     case OFPTYPE_TABLE_FEATURES_REQUEST:
         return OFPERR_OFPBRC_BAD_TYPE;
 
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 28e3863..7a365a6 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -301,6 +301,13 @@ usage(void)
            "  monitor SWITCH [MISSLEN] [invalid_ttl] [watch:[...]]\n"
            "                              print packets received from SWITCH\n"
            "  snoop SWITCH                snoop on SWITCH and its controller\n"
+           "  add-meter SWITCH METER      add meter described by METER\n"
+           "  add-meters SWITCH FILE      add meters from FILE\n"
+           "  mod-meter SWITCH METER      modify specific meter\n"
+           "  del-meters SWITCH [METER]   delete matching METERs\n"
+           "  dump-meters SWITCH [METER]  print matching METERs\n"
+           "  dump-meter-stats SWITCH [METER]  print matching meter stats\n"
+           "  dump-meter-fea SWITCH       print meter features\n"
            "\nFor OpenFlow switches and controllers:\n"
            "  probe TARGET                probe whether TARGET is up\n"
            "  ping TARGET [N]             latency of N-byte echos\n"
@@ -2860,6 +2867,192 @@ ofctl_encode_hello(int argc OVS_UNUSED, char *argv[])
     ofpbuf_delete(hello);
 }
 
+static enum ofputil_protocol
+open_vconn_for_meter(const char *remote, struct vconn **vconnp)
+{
+    enum ofputil_protocol usable_protocols;
+    enum ofputil_protocol cur_protocol;
+    char *usable_s;
+    int i;
+
+    /* Figure out what flow formats will work. */
+    usable_protocols = ofputil_meter_usable_protocols();
+    if (!(usable_protocols & allowed_protocols)) {
+        char *allowed_s = ofputil_protocols_to_string(allowed_protocols);
+        usable_s = ofputil_protocols_to_string(usable_protocols);
+        ovs_fatal(0, "none of the usable flow formats (%s) is among the "
+                  "allowed flow formats (%s)", usable_s, allowed_s);
+    }
+
+    /* If the initial flow format is allowed and usable, keep it. */
+    cur_protocol = open_vconn(remote, vconnp);
+    if (usable_protocols & allowed_protocols & cur_protocol) {
+        return cur_protocol;
+    }
+
+    /* Otherwise try each flow format in turn. */
+    for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
+        enum ofputil_protocol f = 1 << i;
+
+        if (f != cur_protocol
+            && f & usable_protocols & allowed_protocols
+            && try_set_protocol(*vconnp, f, &cur_protocol)) {
+            return f;
+        }
+    }
+
+    usable_s = ofputil_protocols_to_string(usable_protocols);
+    ovs_fatal(0, "switch does not support any of the usable flow "
+              "formats (%s)", usable_s);
+}
+
+static void
+ofctl_meter_mod__(const char *remote, struct ofputil_meter_mod *mms,
+                 size_t n_mms)
+{
+    enum ofputil_protocol protocol;
+    struct vconn *vconn;
+    size_t i;
+
+    protocol = open_vconn_for_meter(remote, &vconn);
+
+    for (i = 0; i < n_mms; i++) {
+        struct ofputil_meter_mod *mm = &mms[i];
+
+        transact_noreply(vconn, ofputil_encode_meter_mod(mm, vconn_get_version(vconn)));
+        free(mm->bands);
+    }
+    vconn_close(vconn);
+}
+
+static void
+ofctl_meter_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
+{
+    struct ofputil_meter_mod *mms = NULL;
+    size_t n_mms = 0;
+
+    parse_ofp_meter_mod_file(argv[2], command, &mms, &n_mms);
+    ofctl_meter_mod__(argv[1], mms, n_mms);
+    free(mms);
+}
+
+static void
+ofctl_meter_mod(int argc, char *argv[], uint16_t command)
+{
+    if (argc > 2 && !strcmp(argv[2], "-")) {
+        ofctl_meter_mod_file(argc, argv, command);
+    } else {
+        struct ofputil_meter_mod mm;
+        parse_ofp_meter_mod_str(&mm, argc > 2 ? argv[2] : "", command, false);
+        ofctl_meter_mod__(argv[1], &mm, 1);
+    }
+}
+
+static void
+ofctl_dump_meters__(int argc, char *argv[])
+{
+    enum ofputil_protocol protocol;
+    struct ofputil_meter_request msr;
+    struct ofpbuf *request;
+    struct vconn *vconn;
+
+    protocol = open_vconn_for_meter(argv[1], &vconn);
+    parse_ofp_meter_request_str(&msr, argc > 2 ? argv[2] : "");
+    request = ofputil_encode_meter_config_request(&msr, vconn_get_version(vconn));
+    if (request) {
+        dump_stats_transaction(vconn, request);
+    } else {
+        printf("Only Openflow 1.3 is supported\n");
+    }
+
+    vconn_close(vconn);
+}
+
+static void
+ofctl_dump_meter_stats__(int argc, char *argv[])
+{
+    enum ofputil_protocol protocol;
+    struct ofpbuf *request;
+    struct vconn *vconn;
+    struct ofputil_meter_request oms;
+
+    protocol = open_vconn_for_meter(argv[1], &vconn);
+
+    parse_ofp_meter_request_str(&oms, argc > 2 ? argv[2] : "");
+    request = ofputil_encode_meter_stats_request(&oms, vconn_get_version(vconn));
+    if (request) {
+        dump_stats_transaction(vconn, request);
+    } else {
+        printf("Only Openflow 1.3 is supported\n");
+    }
+
+    vconn_close(vconn);
+}
+
+static void
+ofctl_dump_meter_features__(int argc, char *argv[])
+{
+    enum ofputil_protocol protocol;
+    struct ofpbuf *request;
+    struct vconn *vconn;
+
+    argc = argc; /* TODO unused parameter */
+
+    protocol = open_vconn_for_meter(argv[1], &vconn);
+
+    request = ofputil_encode_meter_features_request(vconn_get_version(vconn));
+    if (request) {
+        dump_stats_transaction(vconn, request);
+    }
+    else {
+        printf("Only Openflow 1.3 is supported\n");
+    }
+
+    vconn_close(vconn);
+}
+
+static void
+ofctl_add_meter(int argc, char *argv[])
+{
+    ofctl_meter_mod(argc, argv, OFPMC13_ADD);
+}
+
+static void
+ofctl_add_meters(int argc, char *argv[])
+{
+    ofctl_meter_mod_file(argc, argv, OFPMC13_ADD);
+}
+
+static void
+ofctl_mod_meters(int argc, char *argv[])
+{
+    ofctl_meter_mod(argc, argv, OFPMC13_MODIFY);
+}
+
+static void
+ofctl_del_meters(int argc, char *argv[])
+{
+    ofctl_meter_mod(argc, argv, OFPMC13_DELETE);
+}
+
+static void
+ofctl_dump_meters(int argc, char *argv[])
+{
+    return ofctl_dump_meters__(argc, argv);
+}
+
+static void
+ofctl_dump_meter_stats(int argc, char *argv[])
+{
+    return ofctl_dump_meter_stats__(argc, argv);
+}
+
+static void
+ofctl_dump_meter_features(int argc, char *argv[])
+{
+    return ofctl_dump_meter_features__(argc, argv);
+}
+
 static const struct command all_commands[] = {
     { "show", 1, 1, ofctl_show },
     { "monitor", 1, 3, ofctl_monitor },
@@ -2884,6 +3077,14 @@ static const struct command all_commands[] = {
     { "probe", 1, 1, ofctl_probe },
     { "ping", 1, 2, ofctl_ping },
     { "benchmark", 3, 3, ofctl_benchmark },
+
+    { "add-meter", 2, 2, ofctl_add_meter },
+    { "add-meters", 2, 2, ofctl_add_meters },
+    { "mod-meter", 2, 2, ofctl_mod_meters },
+    { "del-meters", 1, 2, ofctl_del_meters },
+    { "dump-meters", 1, 2, ofctl_dump_meters },
+    { "dump-meter-stats", 1, 2, ofctl_dump_meter_stats },
+    { "dump-meter-fea", 1, 1, ofctl_dump_meter_features },
     { "help", 0, INT_MAX, ofctl_help },
 
     /* Undocumented commands for testing. */
-- 
1.7.2.5




More information about the dev mailing list