[ovs-dev] [groups RFC 2/2] Implement OpenFLow 1.1+ "groups" protocol.

Ben Pfaff blp at nicira.com
Fri Jun 28 21:45:55 UTC 2013


From: Neil Zhu <zhuj at centecnetworks.com>

This doesn't include a dpif implementation of groups functionality.  In its
current form, it is untested.

I think I see a memory leak in the group decoder: nothing seems to
free the buckets in some cases.

Signed-off-by: Neil Zhu <zhuj at centecnetworks.com>
Co-authored-by: Ben Pfaff <blp at nicira.com>
Signed-off-by: Ben Pfaff <blp at nicira.com>
---
 AUTHORS                      |    1 +
 lib/learning-switch.c        |    1 +
 lib/ofp-actions.c            |   43 ++++
 lib/ofp-actions.h            |    8 +
 lib/ofp-msgs.h               |   10 +-
 lib/ofp-parse.c              |  210 ++++++++++++++++
 lib/ofp-parse.h              |    9 +-
 lib/ofp-print.c              |  224 ++++++++++++++++-
 lib/ofp-util.c               |  560 +++++++++++++++++++++++++++++++++++++++++-
 lib/ofp-util.def             |    1 +
 lib/ofp-util.h               |   99 ++++++++
 lib/rconn.c                  |    1 +
 ofproto/ofproto-dpif-xlate.c |    4 +
 ofproto/ofproto-dpif.c       |    7 +
 ofproto/ofproto-provider.h   |   35 +++
 ofproto/ofproto.c            |  451 ++++++++++++++++++++++++++++++++-
 utilities/ovs-ofctl.c        |  142 +++++++++++
 17 files changed, 1778 insertions(+), 28 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 26e1f7f..0211b84 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -63,6 +63,7 @@ Mehak Mahajan           mmahajan at nicira.com
 Murphy McCauley         murphy.mccauley at gmail.com
 Natasha Gude            natasha at nicira.com
 Neil McKee              neil.mckee at inmon.com
+Neil Zhu                zhuj at centecnetworks.com
 Paraneetharan Chandrasekaran    paraneetharanc at gmail.com
 Paul Fazzone            pfazzone at nicira.com
 Pavithra Ramesh         paramesh at vmware.com
diff --git a/lib/learning-switch.c b/lib/learning-switch.c
index 352ace3..2f539c6 100644
--- a/lib/learning-switch.c
+++ b/lib/learning-switch.c
@@ -343,6 +343,7 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
     case OFPTYPE_PORT_STATUS:
     case OFPTYPE_PACKET_OUT:
     case OFPTYPE_FLOW_MOD:
+    case OFPTYPE_GROUP_MOD:
     case OFPTYPE_PORT_MOD:
     case OFPTYPE_BARRIER_REQUEST:
     case OFPTYPE_BARRIER_REPLY:
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 899928a..961056d 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -859,6 +859,12 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
         break;
     }
 
+    case OFPUTIL_OFPAT11_GROUP: {
+        struct ofp11_action_group *oag = (struct ofp11_action_group *)a;
+        ofpact_put_GROUP(out)->group_id = ntohl(oag->group_id);
+        break;
+    }
+
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
 #include "ofp-util.def"
         return ofpact_from_nxast(a, code, out);
@@ -944,6 +950,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
     case OFPACT_GOTO_TABLE:
         return OVSINST_OFPIT11_GOTO_TABLE;
     case OFPACT_OUTPUT:
+    case OFPACT_GROUP:
     case OFPACT_CONTROLLER:
     case OFPACT_ENQUEUE:
     case OFPACT_OUTPUT_REG:
@@ -1300,6 +1307,9 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
             return OFPERR_OFPBRC_BAD_TABLE_ID;
         }
         return 0;
+        
+    case OFPACT_GROUP:
+        return 0;
 
     default:
         NOT_REACHED();
@@ -1597,6 +1607,7 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
         ofpact_sample_to_nxast(ofpact_get_SAMPLE(a), out);
         break;
 
+    case OFPACT_GROUP:
     case OFPACT_OUTPUT:
     case OFPACT_ENQUEUE:
     case OFPACT_SET_VLAN_VID:
@@ -1709,6 +1720,9 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
         /* XXX */
         break;
 
+    case OFPACT_GROUP:
+        break;
+
     case OFPACT_CONTROLLER:
     case OFPACT_OUTPUT_REG:
     case OFPACT_BUNDLE:
@@ -1881,6 +1895,11 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_METER:
         NOT_REACHED();
 
+    case OFPACT_GROUP:
+        ofputil_put_OFPAT11_GROUP(out)->group_id =
+            htonl(ofpact_get_GROUP(a)->group_id);
+        break;
+
     case OFPACT_CONTROLLER:
     case OFPACT_OUTPUT_REG:
     case OFPACT_BUNDLE:
@@ -2050,6 +2069,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_GOTO_TABLE:
     case OFPACT_METER:
+    case OFPACT_GROUP:
     default:
         return false;
     }
@@ -2072,6 +2092,24 @@ ofpacts_output_to_port(const struct ofpact *ofpacts, size_t ofpacts_len,
     return false;
 }
 
+/* Returns true if any action in the 'ofpacts_len' bytes of 'ofpacts' outputs
+ * to 'group', false otherwise. */
+bool
+ofpacts_output_to_group(const struct ofpact *ofpacts, size_t ofpacts_len,
+                        uint32_t group_id)
+{
+    const struct ofpact *a;
+
+    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+        if (a->type == OFPACT_GROUP
+            && ofpact_get_GROUP(a)->group_id == group_id) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 bool
 ofpacts_equal(const struct ofpact *a, size_t a_len,
               const struct ofpact *b, size_t b_len)
@@ -2382,6 +2420,11 @@ ofpact_format(const struct ofpact *a, struct ds *s)
                       ovs_instruction_name_from_type(OVSINST_OFPIT13_METER),
                       ofpact_get_METER(a)->meter_id);
         break;
+
+    case OFPACT_GROUP:
+        ds_put_format(s, "group:%"PRIu32,
+                      ofpact_get_GROUP(a)->group_id);
+        break;
     }
 }
 
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index b97afd0..1783511 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -51,6 +51,7 @@
 #define OFPACTS                                                     \
     /* Output. */                                                   \
     DEFINE_OFPACT(OUTPUT,          ofpact_output,        ofpact)    \
+    DEFINE_OFPACT(GROUP,           ofpact_group,         ofpact)    \
     DEFINE_OFPACT(CONTROLLER,      ofpact_controller,    ofpact)    \
     DEFINE_OFPACT(ENQUEUE,         ofpact_enqueue,       ofpact)    \
     DEFINE_OFPACT(OUTPUT_REG,      ofpact_output_reg,    ofpact)    \
@@ -490,6 +491,11 @@ struct ofpact_goto_table {
     uint8_t table_id;
 };
 
+struct ofpact_group {
+    struct ofpact ofpact;
+    uint32_t group_id;
+};
+
 /* Converting OpenFlow to ofpacts. */
 enum ofperr ofpacts_pull_openflow10(struct ofpbuf *openflow,
                                     unsigned int actions_len,
@@ -517,6 +523,8 @@ void ofpacts_put_openflow11_instructions(const struct ofpact[],
 /* Working with ofpacts. */
 bool ofpacts_output_to_port(const struct ofpact[], size_t ofpacts_len,
                             ofp_port_t port);
+bool ofpacts_output_to_group(const struct ofpact[], size_t ofpacts_len,
+                             uint32_t group_id);
 bool ofpacts_equal(const struct ofpact a[], size_t a_len,
                    const struct ofpact b[], size_t b_len);
 
diff --git a/lib/ofp-msgs.h b/lib/ofp-msgs.h
index 3ead49e..47256de 100644
--- a/lib/ofp-msgs.h
+++ b/lib/ofp-msgs.h
@@ -177,6 +177,9 @@ enum ofpraw {
     /* NXT 1.0+ (13): struct nx_flow_mod, uint8_t[8][]. */
     OFPRAW_NXT_FLOW_MOD,
 
+    /* OFPT 1.1+ (15): struct ofp11_group_mod, uint8_t[8][]. */
+    OFPRAW_OFPT11_GROUP_MOD,
+
     /* OFPT 1.0 (15): struct ofp10_port_mod. */
     OFPRAW_OFPT10_PORT_MOD,
     /* OFPT 1.1+ (16): struct ofp11_port_mod. */
@@ -295,15 +298,15 @@ enum ofpraw {
     /* OFPST 1.1+ (6): struct ofp11_group_stats_request. */
     OFPRAW_OFPST11_GROUP_REQUEST,
 
-    /* OFPST 1.1-1.2 (6): struct ofp11_group_stats[]. */
+    /* OFPST 1.1-1.2 (6): uint8_t[8][]. */
     OFPRAW_OFPST11_GROUP_REPLY,
-    /* OFPST 1.3 (6): struct ofp13_group_stats[]. */
+    /* OFPST 1.3 (6): uint8_t[8][]. */
     OFPRAW_OFPST13_GROUP_REPLY,
 
     /* OFPST 1.1+ (7): void. */
     OFPRAW_OFPST11_GROUP_DESC_REQUEST,
 
-    /* OFPST 1.1+ (7): struct ofp11_group_desc_stats[]. */
+    /* OFPST 1.1+ (7): uint8_t[8][]. */
     OFPRAW_OFPST11_GROUP_DESC_REPLY,
 
     /* OFPST 1.2+ (8): void. */
@@ -460,6 +463,7 @@ enum ofptype {
     OFPTYPE_FLOW_MOD,            /* OFPRAW_OFPT10_FLOW_MOD.
                                   * OFPRAW_OFPT11_FLOW_MOD.
                                   * OFPRAW_NXT_FLOW_MOD. */
+    OFPTYPE_GROUP_MOD,           /* OFPRAW_OFPT11_GROUP_MOD. */
     OFPTYPE_PORT_MOD,            /* OFPRAW_OFPT10_PORT_MOD.
                                   * OFPRAW_OFPT11_PORT_MOD. */
 
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index b1e369c..2cba852 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -620,6 +620,10 @@ parse_named_action(enum ofputil_action_code code,
             htons(str_to_u16(arg, "pop_mpls"));
         break;
 
+    case OFPUTIL_OFPAT11_GROUP:
+        ofpact_put_GROUP(ofpacts)->group_id = str_to_u32(arg);
+        break;
+
     case OFPUTIL_NXAST_STACK_PUSH:
         nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg);
         break;
@@ -910,6 +914,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     fm->buffer_id = UINT32_MAX;
     fm->out_port = OFPP_ANY;
     fm->flags = 0;
+    fm->out_group = OFPG11_ANY;
     if (fields & F_ACTIONS) {
         act_str = strstr(string, "action");
         if (!act_str) {
@@ -1354,6 +1359,7 @@ parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
     fsr->cookie_mask = fm.cookie_mask;
     fsr->match = fm.match;
     fsr->out_port = fm.out_port;
+    fsr->out_group = fm.out_group;
     fsr->table_id = fm.table_id;
 }
 
@@ -1437,3 +1443,207 @@ exit:
     }
     return error;
 }
+
+static void
+parse_bucket_str(struct ofputil_bucket* p_bucket, char *str_)
+{
+    struct ofpbuf ofpacts;
+    char *pos, *act, *arg;
+    int n_actions;
+
+    p_bucket->weight = 1;
+    p_bucket->watch_port = OFPP_ANY;
+    p_bucket->watch_group = OFPG11_ANY;
+
+    pos = str_;
+    n_actions = 0;
+    ofpbuf_init(&ofpacts, 64);
+    while (ofputil_parse_key_value(&pos, &act, &arg)) {
+        if (!strcasecmp(act, "weight")) {
+            p_bucket->weight = str_to_u16(arg, "weight");
+            continue;
+        } else if (!strcasecmp(act, "watch_port")) {
+            ofp_port_t port = u16_to_ofp(str_to_u16(arg, "watch_port"));
+            if (ofp_to_u16(port) >= ofp_to_u16(OFPP_MAX) && port != OFPP_ANY) {
+                ovs_fatal(0, "invalid watch_port %s", arg);
+            }
+            p_bucket->watch_port = port;
+            continue;
+        } else if (!strcasecmp(act, "watch_group")) {
+            p_bucket->watch_group = str_to_u32(arg);
+            continue;
+        }
+        if (!str_to_ofpact__(pos, act, arg, &ofpacts, n_actions)) {
+            break;
+        }
+        n_actions++;
+    }
+
+    ofpact_pad(&ofpacts);
+    p_bucket->ofpacts = ofpacts.data;
+    p_bucket->ofpacts_len = ofpacts.size;
+
+}
+
+static void
+parse_group_str(struct ofputil_group_mod *gm, int command, const char *str_,
+              bool verbose)
+{
+    /* refer to parse_ofp_str */
+#define INVALID_TYPE 4
+    enum {
+        F_GROUP_ID    = 1 << 0,
+        F_GROUP_TYPE  = 1 << 1,
+        F_BUCKETS     = 1 << 2,
+    } fields;
+    char *string = NULL;
+    char *save_ptr = NULL;
+    char *bkt_str = NULL;
+    char *next_bkt_str = NULL;
+    char *name;
+
+    switch (command) {
+    case OFPGC11_ADD:
+        fields = F_GROUP_ID | F_GROUP_TYPE | F_BUCKETS;
+        break;
+
+    case OFPGC11_DELETE:
+        fields = F_GROUP_ID;
+        break;
+
+    case OFPGC11_MODIFY:
+        fields = F_GROUP_ID | F_GROUP_TYPE | F_BUCKETS;
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+
+    gm->command = command;
+    gm->group_id = OFPG_ANY;
+    gm->type = INVALID_TYPE;
+    list_init(&gm->buckets);
+    if (command == OFPGC11_DELETE && str_[0] == '\0') {
+        gm->group_id = OFPG_ALL;
+        return;
+    }
+
+    string = xstrdup(str_);
+
+    bkt_str = strstr(string, "bucket");
+    if (fields & F_BUCKETS && bkt_str) {
+        struct ofputil_bucket *p_bucket = NULL;
+
+        *bkt_str = '\0';
+
+        while (bkt_str) {
+            bkt_str = strchr(bkt_str + 1, '=');
+            if (!bkt_str) {
+                ofp_fatal(str_, verbose, "must specify bucket context");
+            }
+            bkt_str++;
+
+            next_bkt_str = strstr(bkt_str, "bucket");
+            if (next_bkt_str) {
+                *next_bkt_str = '\0';
+            }
+
+            p_bucket = xzalloc(sizeof(struct ofputil_bucket));
+            parse_bucket_str(p_bucket, bkt_str);
+            list_push_back(&gm->buckets, &p_bucket->list_node);
+
+            bkt_str = next_bkt_str;
+        }
+    }
+
+    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);
+        }
+
+        if (!strcasecmp(name, "group_id")) {
+            if(!strcasecmp(value, "all")) {
+                gm->group_id = OFPG_ALL;
+            } else {
+                gm->group_id = str_to_u32(value);
+                if ((gm->group_id != OFPG_ALL) &&
+                    (gm->group_id > OFPG_MAX)) {
+                    ofp_fatal(str_, verbose, "invalid group id %"PRIu32"",
+                              gm->group_id);
+                }
+            }
+        } else if (!strcasecmp(name, "type")){
+            if (!(fields & F_GROUP_TYPE)) {
+                ofp_fatal(str_, verbose, "type is not needed");
+            }
+            if (!strcasecmp(value, "all")) {
+                gm->type = OFPGT11_ALL;
+            } else if (!strcasecmp(value, "select")) {
+                gm->type = OFPGT11_SELECT;
+            } else if (!strcasecmp(value, "indirect")) {
+                gm->type = OFPGT11_INDIRECT;
+            } else if (!strcasecmp(value, "ff") ||
+                       !strcasecmp(value, "fast_failover")) {
+                gm->type = OFPGT11_FF;
+            } else {
+                ofp_fatal(str_, verbose, "invalid group type %s", value);
+            }
+        } else if (!strcasecmp(name, "bucket")) {
+            ofp_fatal(str_, verbose, "bucket is not needed");
+        } else {
+            ofp_fatal(str_, verbose, "unknown keyword %s", name);
+        }
+    }
+    if (fields & F_GROUP_ID) {
+        if (gm->group_id == OFPG_ANY) {
+            ofp_fatal(str_, verbose, "must specify a group_id");
+        }
+    }
+    if (fields & F_GROUP_TYPE) {
+        if (gm->type == INVALID_TYPE) {
+            ofp_fatal(str_, verbose, "must specify a type");
+        }
+    }
+
+    free(string);
+}
+
+void
+parse_ofp_group_mod_str(struct ofputil_group_mod *gm, const char *string,
+                       uint16_t command, bool verbose)
+{
+    parse_group_str(gm, command, string, verbose);
+}
+
+void
+parse_ofp_group_mod_file(const char *file_name, uint16_t command,
+                        struct ofputil_group_mod **gms, size_t *n_gms)
+{
+    size_t allocated_fms;
+    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_fms = *n_gms;
+    ds_init(&s);
+    while (!ds_get_preprocessed_line(&s, stream)) {
+        if (*n_gms >= allocated_fms) {
+            *gms = x2nrealloc(*gms, &allocated_fms, sizeof **gms);
+        }
+        parse_ofp_group_mod_str(&(*gms)[*n_gms], ds_cstr(&s), command, false);
+        *n_gms += 1;
+    }
+    ds_destroy(&s);
+
+    if (stream != stdin) {
+        fclose(stream);
+    }
+}
diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h
index 6ee25a4..4150b23 100644
--- a/lib/ofp-parse.h
+++ b/lib/ofp-parse.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@ struct ofpbuf;
 struct ofputil_flow_mod;
 struct ofputil_flow_monitor_request;
 struct ofputil_flow_stats_request;
+struct ofputil_group_mod;
 struct ofputil_meter_mod;
 
 void parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
@@ -52,4 +53,10 @@ void parse_ofp_meter_mod_str(struct ofputil_meter_mod *, const char *string,
 void parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
                                 const char *);
 
+void parse_ofp_group_mod_file(const char *file_name, uint16_t command,
+                              struct ofputil_group_mod **gms, size_t *n_gms);
+
+void parse_ofp_group_mod_str(struct ofputil_group_mod *, const char *string,
+                             uint16_t command, bool verbose);
+
 #endif /* ofp-parse.h */
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 1d8b98c..2623daa 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -2129,6 +2129,199 @@ ofp_print_not_implemented(struct ds *string)
 }
 
 static void
+ofp_print_group(struct ds *s, uint32_t group_id, uint8_t type,
+                struct list *p_buckets)
+{
+    static const char *type_str[] = { "ALL", "SELECT", "INDIRECT",
+                                      "FF", "UNKNOWN" };
+    struct ofputil_bucket *bucket;
+
+    ds_put_format(s, "group_id=%"PRIu32",type=%s",
+                  group_id, type_str[type>3?4:type]);
+    if (!p_buckets) {
+        return;
+    }
+
+    LIST_FOR_EACH (bucket, list_node, p_buckets) {
+        ds_put_cstr(s, ",bucket=");
+
+        if (bucket->weight != 1) {
+            ds_put_format(s, "weight:%"PRIu16",", bucket->weight);
+        }
+        if (bucket->watch_port != OFPP_NONE) {
+            ds_put_format(s, "watch_port:%"PRIu32",", bucket->watch_port);
+        }
+        if (bucket->watch_group != OFPG11_ANY) {
+            ds_put_format(s, "watch_group:%"PRIu32",", bucket->watch_group);
+        }
+
+        ds_put_cstr(s, "actions=");
+        ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, s);
+    }
+}
+
+static void
+ofp_print_group_desc(struct ds *s, 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_OFPST11_GROUP_DESC_REPLY) {
+        ds_put_cstr(s, " ***error reply type***");
+        return;
+    }
+
+    for (;;) {
+        struct ofputil_group_desc gd;
+        int retval;
+
+        retval = ofputil_decode_group_desc_reply(&gd, &b);
+        if (retval) {
+            if (retval != EOF) {
+                ds_put_cstr(s, " ***parse error***");
+            }
+            break;
+        }
+
+        ds_put_char(s, '\n');
+        ds_put_char(s, ' ');
+        ofp_print_group(s, gd.group_id, gd.type, &gd.buckets);
+     }
+}
+
+static void
+ofp_print_group_stats(struct ds *s, const struct ofp_header *oh)
+{
+    struct ofpbuf b;
+    enum ofpraw raw;
+    size_t bucket_i;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+
+    for (;;) {
+        struct ofputil_group_stats gs;
+        int retval;
+
+        memset(&gs, 0, sizeof(gs));
+        retval = ofputil_decode_group_stats_reply(raw, &gs, &b);
+        if (retval) {
+            if (retval != EOF) {
+                ds_put_cstr(s, " ***parse error***");
+            }
+            break;
+        }
+
+        ds_put_char(s, '\n');
+
+        ds_put_char(s, ' ');
+        ds_put_format(s, "group_id=%"PRIu32",", gs.group_id);
+
+        if (oh->version >= OFP13_VERSION) {
+            ds_put_cstr(s, "duration=");
+            ofp_print_duration(s, gs.duration_sec, gs.duration_nsec);
+        }
+        ds_put_format(s, ",ref_count=%"PRIu32",", gs.ref_count);
+        ds_put_format(s, "packet_count=%"PRIu64",", gs.packet_count);
+        ds_put_format(s, "byte_count=%"PRIu64"", gs.byte_count);
+
+        for (bucket_i = 0; bucket_i < gs.n_buckets; bucket_i++) {
+            if (gs.bucket_stats[bucket_i].packet_count != UINT64_MAX) {
+                ds_put_format(s, ",bucket%"PRIu32":", bucket_i);
+                ds_put_format(s, "packet_count=%"PRIu64",", gs.bucket_stats[bucket_i].packet_count);
+                ds_put_format(s, "byte_count=%"PRIu64"", gs.bucket_stats[bucket_i].byte_count);
+            }
+        }
+     }
+}
+
+static void
+ofp_print_group_features(struct ds *string, const struct ofp_header *oh)
+{
+    struct ofputil_group_features features;
+    enum ofperr error;
+    struct ofpbuf b;
+
+    error = ofputil_decode_group_features_reply(oh, &features, &b);
+    if (error) {
+        ofp_print_error(string, error);
+        return;
+    }
+
+    ds_put_format(string, "\n Group table:\n");
+    ds_put_format(string, "    Types:  0x%"PRIx32"\n", features.types);
+    ds_put_format(string, "    Capabilities:  0x%"PRIx32"\n",
+                  features.capabilities);
+
+    if (features.types & (1u << OFPGT11_ALL)) {
+        ds_put_format(string, "    All group :\n");
+        ds_put_format(string,
+                      "        max_groups = %"PRIx32" actions=0x%08"PRIx32"\n",
+                      features.max_groups[0], features.actions[0]);
+    }
+
+    if (features.types & (1u << OFPGT11_SELECT)) {
+        ds_put_format(string, "    Select group :\n");
+        ds_put_format(string, "        max_groups = 0x%"PRIx32" "
+                      "actions=0x%08"PRIx32"\n",
+                      features.max_groups[1], features.actions[1]);
+    }
+
+    if (features.types & (1u << OFPGT11_INDIRECT)) {
+        ds_put_format(string, "    Indirect group :\n");
+        ds_put_format(string, "        max_groups = 0x%"PRIx32" "
+                      "actions=0x%08"PRIx32"\n",
+                      features.max_groups[2], features.actions[2]);
+    }
+
+    if (features.types & (1u << OFPGT11_FF)) {
+        ds_put_format(string, "    Fast Failover group : \n");
+        ds_put_format(string, "        max_groups = 0x%"PRIx32" "
+                      "actions=0x%08"PRIx32"\n",
+                      features.max_groups[3], features.actions[3]);
+    }
+}
+
+static void
+ofp_print_group_mod(struct ds *s, const struct ofp_header *oh)
+{
+    struct ofputil_group_mod gm;
+    int error;
+
+    error = ofputil_decode_group_mod(oh, &gm);
+    if (error) {
+        ofp_print_error(s, error);
+        return;
+    }
+
+    ds_put_char(s, '\n');
+
+    ds_put_char(s, ' ');
+    switch (gm.command) {
+    case OFPGC11_ADD:
+        ds_put_cstr(s, "ADD");
+        break;
+
+    case OFPGC11_MODIFY:
+        ds_put_cstr(s, "MOD");
+        break;
+
+    case OFPGC11_DELETE:
+        ds_put_cstr(s, "DEL");
+        break;
+
+    default:
+        ds_put_format(s, "cmd:%"PRIu16"", gm.command);
+    }
+    ds_put_char(s, ' ');
+
+    ofp_print_group(s, gm.group_id, gm.type, &gm.buckets);
+}
+
+static void
 ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
                 struct ds *string, int verbosity)
 {
@@ -2137,17 +2330,38 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
     ofp_header_to_string__(oh, raw, string);
     switch (ofptype_from_ofpraw(raw)) {
 
-        /* FIXME: Change the following once they are implemented: */
-    case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
-    case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
-    case OFPTYPE_GET_ASYNC_REQUEST:
-    case OFPTYPE_GET_ASYNC_REPLY:
     case OFPTYPE_GROUP_STATS_REQUEST:
+        ofp_print_stats_request(string, oh);
+        break;
+
     case OFPTYPE_GROUP_STATS_REPLY:
+        ofp_print_group_stats(string, oh);
+        break;
+
     case OFPTYPE_GROUP_DESC_STATS_REQUEST:
+        ofp_print_stats_request(string, oh);
+        break;
+
     case OFPTYPE_GROUP_DESC_STATS_REPLY:
+        ofp_print_group_desc(string, oh);
+        break;
+
     case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
+        ofp_print_stats_request(string, oh);
+        break;
+
     case OFPTYPE_GROUP_FEATURES_STATS_REPLY:
+        ofp_print_group_features(string, oh);
+        break;
+
+    case OFPTYPE_GROUP_MOD:
+        ofp_print_group_mod(string, oh);
+        break;
+
+    case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
+    case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
+    case OFPTYPE_GET_ASYNC_REQUEST:
+    case OFPTYPE_GET_ASYNC_REPLY:
     case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
     case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
         ofp_print_not_implemented(string);
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index aa4009d..162fb89 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -1532,6 +1532,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
         if (error) {
             return error;
         }
+        fm->out_group = ntohl(ofm->out_group);
+
         if ((ofm->command == OFPFC_DELETE
              || ofm->command == OFPFC_DELETE_STRICT)
             && ofm->out_group != htonl(OFPG_ANY)) {
@@ -1572,6 +1574,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             fm->hard_timeout = ntohs(ofm->hard_timeout);
             fm->buffer_id = ntohl(ofm->buffer_id);
             fm->out_port = u16_to_ofp(ntohs(ofm->out_port));
+            fm->out_group = OFPG11_ANY;
             fm->flags = ntohs(ofm->flags);
         } else if (raw == OFPRAW_NXT_FLOW_MOD) {
             /* Nicira extended flow_mod. */
@@ -1603,6 +1606,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             fm->hard_timeout = ntohs(nfm->hard_timeout);
             fm->buffer_id = ntohl(nfm->buffer_id);
             fm->out_port = u16_to_ofp(ntohs(nfm->out_port));
+            fm->out_group = OFPG11_ANY;
             fm->flags = ntohs(nfm->flags);
         } else {
             NOT_REACHED();
@@ -2038,7 +2042,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         ofm->priority = htons(fm->priority);
         ofm->buffer_id = htonl(fm->buffer_id);
         ofm->out_port = ofputil_port_to_ofp11(fm->out_port);
-        ofm->out_group = htonl(OFPG11_ANY);
+        ofm->out_group = htonl(fm->out_group);
         ofm->flags = htons(fm->flags);
         oxm_put_match(msg, &fm->match);
         ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len, msg);
@@ -2135,6 +2139,7 @@ ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr,
     fsr->aggregate = aggregate;
     ofputil_match_from_ofp10_match(&ofsr->match, &fsr->match);
     fsr->out_port = u16_to_ofp(ntohs(ofsr->out_port));
+    fsr->out_group = OFPG11_ANY;
     fsr->table_id = ofsr->table_id;
     fsr->cookie = fsr->cookie_mask = htonll(0);
 
@@ -2155,9 +2160,7 @@ ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
     if (error) {
         return error;
     }
-    if (ofsr->out_group != htonl(OFPG11_ANY)) {
-        return OFPERR_OFPFMFC_UNKNOWN;
-    }
+    fsr->out_group = ntohl(ofsr->out_group);
     fsr->cookie = ofsr->cookie;
     fsr->cookie_mask = ofsr->cookie_mask;
     error = ofputil_pull_ofp11_match(b, &fsr->match, NULL);
@@ -2187,6 +2190,7 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
 
     fsr->aggregate = aggregate;
     fsr->out_port = u16_to_ofp(ntohs(nfsr->out_port));
+    fsr->out_group = OFPG11_ANY;
     fsr->table_id = nfsr->table_id;
 
     return 0;
@@ -2252,7 +2256,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
         ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
         ofsr->table_id = fsr->table_id;
         ofsr->out_port = ofputil_port_to_ofp11(fsr->out_port);
-        ofsr->out_group = htonl(OFPG11_ANY);
+        ofsr->out_group = htonl(fsr->out_group);
         ofsr->cookie = fsr->cookie;
         ofsr->cookie_mask = fsr->cookie_mask;
         oxm_put_match(msg, &fsr->match);
@@ -5184,6 +5188,552 @@ ofputil_decode_port_stats_request(const struct ofp_header *request,
     }
 }
 
+struct ofpbuf *
+ofputil_encode_group_stats_request(enum ofp_version ofp_version, uint32_t group_id)
+{
+    struct ofpbuf *request;
+
+    switch (ofp_version) {
+    case OFP10_VERSION: {
+        ovs_fatal(0, "dump-gstats need argument \'-O openflow11(+)\'");
+    }
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
+        struct ofp11_group_stats_request *req;
+        request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_REQUEST, ofp_version, 0);
+        req = ofpbuf_put_zeros(request, sizeof *req);
+        req->group_id = htonl(group_id);
+        break;
+    }
+    default:
+        NOT_REACHED();
+    }
+
+    return request;
+}
+
+struct ofpbuf *
+ofputil_encode_group_desc_request(enum ofp_version ofp_version)
+{
+    struct ofpbuf *request;
+
+    switch (ofp_version) {
+    case OFP10_VERSION: {
+        ovs_fatal(0, "dump-groups need argument \'-O openflow11(+)\'");
+    }
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
+        request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_DESC_REQUEST, ofp_version, 0);
+        break;
+    }
+    default:
+        NOT_REACHED();
+    }
+
+    return request;
+}
+
+static void
+ofputil_group_stats_to_ofp11(const struct ofputil_group_stats *ogs,
+                            struct ofp11_group_stats *gs11)
+{
+    gs11->length = htons(sizeof *gs11
+                         + (sizeof(struct ofp11_bucket_counter)
+                            * ogs->n_buckets));
+    memset(gs11->pad, 0, sizeof gs11->pad);
+    gs11->group_id = htonl(ogs->group_id);
+    gs11->ref_count = htonl(ogs->ref_count);
+    memset(gs11->pad2, 0, sizeof gs11->pad2);
+    gs11->packet_count = htonll(ogs->packet_count);
+    gs11->byte_count = htonll(ogs->byte_count);
+}
+
+static void
+ofputil_group_stats_to_ofp13(const struct ofputil_group_stats *ogs,
+                            struct ofp13_group_stats *gs13)
+{
+    gs13->gs.length = htons(sizeof *gs13
+                            + (sizeof(struct ofp11_bucket_counter)
+                               * ogs->n_buckets));
+    memset(gs13->gs.pad, 0, sizeof gs13->gs.pad);
+    gs13->gs.group_id = htonl(ogs->group_id);
+    gs13->gs.ref_count = htonl(ogs->ref_count);
+    memset(gs13->gs.pad2, 0, sizeof gs13->gs.pad2);
+    gs13->gs.packet_count = htonll(ogs->packet_count);
+    gs13->gs.byte_count = htonll(ogs->byte_count);
+    gs13->duration_sec = htonl(ogs->duration_sec);
+    gs13->duration_nsec = htonl(ogs->duration_nsec);
+}
+
+/* Encode a group stat for 'ogs' and append it to 'replies'. */
+void
+ofputil_append_group_stats(struct list *replies,
+                         const struct ofputil_group_stats *ogs)
+{
+    struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
+    struct ofp_header *oh = msg->data;
+    int32_t bucket_i;
+
+    switch ((enum ofp_version)oh->version) {
+    case OFP13_VERSION: {
+        struct ofp13_group_stats *reply = ofpmp_append(replies, sizeof *reply);
+        ofputil_group_stats_to_ofp13(ogs, reply);
+        for (bucket_i = 0; bucket_i < ogs->n_buckets; bucket_i++) {
+            struct ofp11_bucket_counter* bucket = ofpmp_append(replies, sizeof *bucket);
+            bucket->packet_count = htonll(ogs->bucket_stats[bucket_i].packet_count);
+            bucket->byte_count = htonll(ogs->bucket_stats[bucket_i].byte_count);
+        }
+        
+        break;
+    }
+        
+    case OFP12_VERSION:
+    case OFP11_VERSION: {
+        struct ofp11_group_stats *reply = ofpmp_append(replies, sizeof *reply);
+        ofputil_group_stats_to_ofp11(ogs, reply);
+        for (bucket_i = 0; bucket_i < ogs->n_buckets; bucket_i++) {
+            struct ofp11_bucket_counter* bucket = ofpmp_append(replies, sizeof *bucket);
+            bucket->packet_count = htonll(ogs->bucket_stats[bucket_i].packet_count);
+            bucket->byte_count = htonll(ogs->bucket_stats[bucket_i].byte_count);
+        }
+        break;
+    }
+
+    case OFP10_VERSION: 
+        break;
+ 
+    default:
+        NOT_REACHED();
+    }
+}
+
+struct ofpbuf *
+ofputil_encode_group_features_request(enum ofp_version ofp_version)
+{
+    struct ofpbuf *request = NULL;
+
+    switch (ofp_version) {
+    case OFP10_VERSION: 
+    case OFP11_VERSION:{
+        ovs_fatal(0, "dump-gfeatures need argument \'-O openflow12(+)\'");
+    }
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
+        request = ofpraw_alloc(OFPRAW_OFPST12_GROUP_FEATURES_REQUEST, 
+                                        ofp_version, 0);
+        break;
+    }
+    default:
+        NOT_REACHED();
+    }
+
+    return request;
+}
+
+struct ofpbuf *
+ofputil_encode_group_features_reply(const struct ofputil_group_features *features,
+                                 const struct ofp_header *request)
+{
+    struct ofpbuf *reply;
+    enum ofpraw raw;
+    struct ofp12_group_features_stats *ogf;
+    
+    switch ((enum ofp_version) request->version) {
+    case OFP10_VERSION:
+    case OFP11_VERSION:
+        ovs_fatal(0, "Group need argument \'-O Openflow12\'");
+        
+    case OFP12_VERSION:
+    case OFP13_VERSION:
+        raw = OFPRAW_OFPST12_GROUP_FEATURES_REPLY;
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+
+    reply = ofpraw_alloc_xid(raw, (enum ofp_version) request->version, 
+                        request->xid, 0);
+    ogf = ofpbuf_put_zeros(reply, sizeof *ogf);
+    ogf->types = htonl(features->types);
+    ogf->capabilities = htonl(features->capabilities);
+    ogf->max_groups[0] = htonl(features->max_groups[0]);
+    ogf->max_groups[1] = htonl(features->max_groups[1]);
+    ogf->max_groups[2] = htonl(features->max_groups[2]);
+    ogf->max_groups[3] = htonl(features->max_groups[3]);
+    ogf->actions[0] = htonl(features->actions[0]);
+    ogf->actions[1] = htonl(features->actions[1]);
+    ogf->actions[2] = htonl(features->actions[2]);
+    ogf->actions[3] = htonl(features->actions[3]);
+
+    return reply;
+}
+
+enum ofperr
+ofputil_decode_group_features_reply(const struct ofp_header *oh,
+                               struct ofputil_group_features *features,
+                               struct ofpbuf *b)
+{
+    const struct ofp12_group_features_stats *ogf;
+    enum ofpraw raw;
+
+    ofpbuf_use_const(b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(b);
+
+    ogf = ofpbuf_pull(b, sizeof *ogf);
+
+    if (raw == OFPRAW_OFPST12_GROUP_FEATURES_REPLY) {
+        features->types = ntohl(ogf->types);
+        features->capabilities = ntohl(ogf->capabilities);
+        features->max_groups[0] = ntohl(ogf->max_groups[0]);
+        features->max_groups[1] = ntohl(ogf->max_groups[1]);
+        features->max_groups[2] = ntohl(ogf->max_groups[2]);
+        features->max_groups[3] = ntohl(ogf->max_groups[3]);
+        features->actions[0] = ntohl(ogf->actions[0]);
+        features->actions[1] = ntohl(ogf->actions[1]);
+        features->actions[2] = ntohl(ogf->actions[2]);
+        features->actions[3] = ntohl(ogf->actions[3]);
+    } else {
+        return OFPERR_OFPBRC_BAD_VERSION;
+    }
+
+    return 0;
+}
+
+/* Parse a group status request message into a 32 bit OpenFlow 1.1
+ * group ID and stores the latter in '*group_id'.
+ * Returns 0 if successful, otherwise an OFPERR_* number. */
+enum ofperr
+ofputil_decode_group_stats_request(const struct ofp_header *request,
+                                  uint32_t *group_id)
+{
+    switch ((enum ofp_version)request->version) {
+    case OFP13_VERSION:
+    case OFP12_VERSION:
+    case OFP11_VERSION: {
+        const struct ofp11_group_stats_request *gsr11 = ofpmsg_body(request);
+        *group_id = ntohl(gsr11->group_id);
+        return 0;
+    }
+
+    case OFP10_VERSION: 
+        return OFPERR_OFPGMFC_INVALID_GROUP;
+            
+    default:
+        NOT_REACHED();
+    }
+}
+
+
+int
+ofputil_decode_group_stats_reply(uint32_t raw,
+                               struct ofputil_group_stats *ogs,
+                               struct ofpbuf *b)
+{
+    struct ofp11_group_stats *ogs11 = NULL;
+    struct ofp13_group_stats *ogs13 = NULL;
+    struct ofp11_bucket_counter* obc = NULL;
+    size_t bucket_i;
+
+    if (!b->size) {
+        return EOF;
+    }
+    
+    if (raw == OFPRAW_OFPST11_GROUP_REPLY) {
+        ogs11 = ofpbuf_try_pull(b, sizeof *ogs11);
+        if(!ogs11) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP reply has %zu leftover "
+                         "bytes at end", b->size);
+            return EINVAL;
+        }
+        ogs->group_id = ntohl(ogs11->group_id);
+        ogs->ref_count = ntohl(ogs11->ref_count);
+        ogs->packet_count = ntohll(ogs11->packet_count);
+        ogs->byte_count = ntohll(ogs11->byte_count);
+
+        if (ntohs(ogs11->length) < sizeof *ogs11) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP reply claims invalid "
+                         "length %zu", ogs13->gs.length);
+            return EINVAL;
+        }
+        ogs->n_buckets = ((ntohs(ogs11->length) - sizeof *ogs11)
+                          / sizeof(struct ofp11_bucket_counter));
+
+        obc = ofpbuf_try_pull(b, ogs->n_buckets * sizeof(struct ofp11_bucket_counter));
+
+        if (!obc) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP reply has %zu leftover "
+                         "bytes at end", b->size);
+            return EINVAL;
+        }
+        for (bucket_i = 0; bucket_i < ogs->n_buckets; bucket_i++) {
+            ogs->bucket_stats[bucket_i].packet_count = ntohll(obc[bucket_i].packet_count);
+            ogs->bucket_stats[bucket_i].byte_count = ntohll(obc[bucket_i].byte_count);
+        }
+        
+    } else if (raw == OFPRAW_OFPST13_GROUP_REPLY) {
+        ogs13 = ofpbuf_try_pull(b, sizeof *ogs13);
+        if(!ogs13) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST13_GROUP reply has %zu leftover "
+                         "bytes at end", b->size);
+            return EINVAL;
+        }
+        ogs->group_id = ntohl(ogs13->gs.group_id);
+        ogs->ref_count = ntohl(ogs13->gs.ref_count);
+        ogs->packet_count = ntohll(ogs13->gs.packet_count);
+        ogs->byte_count = ntohll(ogs13->gs.byte_count);
+        ogs->duration_sec = ntohl(ogs13->duration_sec);
+        ogs->duration_nsec = ntohl(ogs13->duration_nsec);
+
+        if (ntohs(ogs13->gs.length) < sizeof *ogs13) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST13_GROUP reply claims invalid "
+                         "length %zu", ogs13->gs.length);
+            return EINVAL;
+        }
+        ogs->n_buckets = ((ntohs(ogs13->gs.length) - sizeof *ogs13)
+                          / sizeof(struct ofp11_bucket_counter));
+
+        obc = ofpbuf_try_pull(b, ogs->n_buckets * sizeof(struct ofp11_bucket_counter));
+
+        if (!obc) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST13_GROUP reply has %zu leftover "
+                         "bytes at end", b->size);
+            return EINVAL;
+        }
+        for (bucket_i = 0; bucket_i < ogs->n_buckets; bucket_i++) {
+            ogs->bucket_stats[bucket_i].packet_count = ntohll(obc[bucket_i].packet_count);
+            ogs->bucket_stats[bucket_i].byte_count = ntohll(obc[bucket_i].byte_count);
+        }
+    }
+    
+    return 0;
+}
+
+
+/* Appends an OFPST_FLOW reply that contains the data in 'fs' to
+ * those already present in the list of ofpbufs in 'replies'.  'replies' should
+ * have been initialized with ofputil_start_stats_reply(). */
+void
+ofputil_append_group_desc_reply(const struct ofputil_group_desc *gds,
+                                struct list* buckets,
+                                struct list *replies)
+{
+    struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
+    size_t start_ogds = reply->size;
+    size_t start_bucket;
+    enum ofpraw raw;
+    struct ofputil_bucket *bucket;
+    
+    ofpraw_decode_partial(&raw, reply->data, reply->size);
+    if (raw == OFPRAW_OFPST11_GROUP_DESC_REPLY) {
+        struct ofp11_group_desc_stats *ogds;
+        struct ofp11_bucket *ob;
+        ofpbuf_put_uninit(reply, sizeof *ogds);
+
+        LIST_FOR_EACH(bucket, list_node, buckets) {
+            start_bucket = reply->size;
+            ofpbuf_put_uninit(reply, sizeof *ob);
+            ofpacts_put_openflow11_actions(bucket->ofpacts, 
+                            bucket->ofpacts_len, reply);
+            ob = ofpbuf_at_assert(reply, start_bucket, sizeof *ob);
+            ob->len = htons(reply->size - start_bucket);
+            ob->weight = htons(bucket->weight);
+            /* Modified by yanxa, for bug 22571, 2013-3-21 */
+            ob->watch_port = ofputil_port_to_ofp11(bucket->watch_port);
+            ob->watch_group = htonl(bucket->watch_group);
+        }
+        ogds = ofpbuf_at_assert(reply, start_ogds, sizeof *ogds);
+        ogds->length = htons(reply->size - start_ogds);
+        ogds->type = gds->type;
+        ogds->pad = 0;
+        ogds->group_id = htonl(gds->group_id);
+    } else {
+        NOT_REACHED();
+    }
+
+    ofpmp_postappend(replies, start_ogds);
+    return;
+}
+
+
+static enum ofperr
+ofputil_pull_bucket11(struct ofpbuf *b, uint16_t group_length, struct list* buckets)
+{
+    enum ofperr error;
+    struct ofp11_bucket *ob;
+    struct ofpbuf ofpacts;
+    int action_len;
+    struct ofputil_bucket *bucket;
+    size_t start_group = b->size;
+    ofp_port_t ofp10_port;
+    
+    while((b->size >= sizeof(struct ofp11_bucket)) && 
+          (start_group - b->size) < group_length)
+    {
+        ob = ofpbuf_pull(b, sizeof *ob);
+        if (!ob || (ntohs(ob->len) < sizeof(struct ofp11_bucket) )) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bucket length %u is not a valid length ",
+                ob->len);
+            return OFPERR_OFPGMFC_BAD_BUCKET;
+        }
+        action_len = ntohs(ob->len) - sizeof (struct ofp11_bucket);
+        ofpbuf_init(&ofpacts, 0);
+        error = ofpacts_pull_openflow11_actions(b, action_len, &ofpacts);
+        if (error) {
+            ofpbuf_uninit(&ofpacts);
+            return error;
+        }
+
+        bucket = xzalloc(sizeof *bucket);
+        bucket->weight = ntohs(ob->weight);
+        /* Modified by yanxa, for bug 22571, 2013-3-21 */
+        error = ofputil_port_from_ofp11(ob->watch_port, &ofp10_port);
+        if (error) {
+            ofpbuf_uninit(&ofpacts);
+            return OFPERR_OFPGMFC_BAD_WATCH;
+        }
+        bucket->watch_port = ofp10_port;
+        bucket->watch_group = ntohl(ob->watch_group);
+        bucket->ofpacts = xmemdup(ofpacts.data, ofpacts.size);
+        bucket->ofpacts_len = ofpacts.size;
+        list_push_back(buckets, &bucket->list_node);
+        ofpbuf_uninit(&ofpacts);
+    }
+
+    return 0;
+}
+
+int
+ofputil_decode_group_desc_reply(struct ofputil_group_desc *ogd,
+                               struct ofpbuf *b)
+{
+    struct ofp11_group_desc_stats *ogds = NULL;
+
+    if (!b->size) {
+        return EOF;
+    }
+    
+    ogds = ofpbuf_try_pull(b, sizeof *ogds);
+    if(!ogds) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply has %zu leftover "
+                     "bytes at end", b->size);
+        return EINVAL;
+    }
+    ogd->length = ntohs(ogds->length);
+    ogd->type = ogds->type;
+    ogd->group_id = ntohl(ogds->group_id);
+    list_init(&ogd->buckets);
+
+    if (ntohs(ogds->length) < sizeof *ogds) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply claims invalid "
+                     "length %zu", ogds->length);
+        return EINVAL;
+    }
+    if (ntohs(ogds->length) - sizeof *ogds > b->size) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply claims invalid "
+                     "length %zu", ogds->length);
+        return EINVAL;
+    }
+
+    ofputil_pull_bucket11(b, ogd->length - sizeof(*ogds), &ogd->buckets);
+    
+    return 0;
+}
+
+struct ofpbuf *
+ofputil_encode_group_mod(enum ofp_version ofp_version, const struct ofputil_group_mod *gm)
+{
+    struct ofpbuf *b;
+    struct ofp11_group_mod *ogm;
+    size_t start_ogm;
+    size_t start_bucket;
+    struct ofputil_bucket *bucket;
+    struct ofp11_bucket *ob;
+
+    switch (ofp_version) {
+    case OFP10_VERSION: {
+        if (gm->command == OFPGC11_ADD) {
+            ovs_fatal(0, "add-group need argument \'-O openflow11(+)\'");
+        } else if (gm->command == OFPGC11_MODIFY) {
+            ovs_fatal(0, "mod-group need argument \'-O openflow11(+)\'");
+        } else {
+            ovs_fatal(0, "del-groups need argument \'-O openflow11(+)\'");
+        }
+    }
+
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+    case OFP13_VERSION: {
+        b = ofpraw_alloc(OFPRAW_OFPT11_GROUP_MOD, ofp_version, 0);
+        start_ogm = b->size;
+        ofpbuf_put_uninit(b, sizeof *ogm);
+
+        LIST_FOR_EACH (bucket, list_node, &gm->buckets) {
+            start_bucket = b->size;
+            ofpbuf_put_uninit(b, sizeof *ob);
+            if (bucket->ofpacts && bucket->ofpacts_len) {
+                ofpacts_put_openflow11_actions(bucket->ofpacts, 
+                                bucket->ofpacts_len, b);
+            }
+            ob = ofpbuf_at_assert(b, start_bucket, sizeof *ob);
+            ob->len = htons(b->size - start_bucket);;
+            ob->weight = htons(bucket->weight);
+            ob->watch_port = ofputil_port_to_ofp11(bucket->watch_port);
+            ob->watch_group = htonl(bucket->watch_group);
+        }
+        ogm = ofpbuf_at_assert(b, start_ogm, sizeof *ogm);
+        ogm->command = htons(gm->command);
+        ogm->type = gm->type;
+        ogm->pad = 0;
+        ogm->group_id = htonl(gm->group_id);
+
+        break;
+    }
+
+    default:
+        NOT_REACHED();
+    }
+
+    return b;
+}
+
+enum ofperr
+ofputil_decode_group_mod(const struct ofp_header *oh,
+                        struct ofputil_group_mod *gm)
+{
+    enum ofpraw raw;
+    struct ofpbuf b;
+    enum ofperr error;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+
+    if (raw == OFPRAW_OFPT11_GROUP_MOD) {
+        const struct ofp11_group_mod *ogm;
+        uint32_t group_length;
+
+        ogm = ofpbuf_pull(&b, sizeof *ogm);
+        gm->command = ntohs(ogm->command);
+        gm->type = ogm->type;
+        gm->group_id = ntohl(ogm->group_id);
+        list_init(&gm->buckets);
+        
+        /* Now get the buckets. */
+        group_length = ntohs(oh->length) - sizeof *oh - sizeof *ogm;
+        error = ofputil_pull_bucket11(&b, group_length, &gm->buckets);
+        if (error) {
+            return error;
+        }
+    } else {
+        return OFPERR_OFPBRC_BAD_TYPE;
+    }
+
+    return 0;
+}
+
+
 /* Parse a queue status request message into 'oqsr'.
  * Returns 0 if successful, otherwise an OFPERR_* number. */
 enum ofperr
diff --git a/lib/ofp-util.def b/lib/ofp-util.def
index d88d420..f5eec73 100644
--- a/lib/ofp-util.def
+++ b/lib/ofp-util.def
@@ -40,6 +40,7 @@ OFPAT11_ACTION(OFPAT11_SET_QUEUE,    ofp11_action_set_queue, 0, "set_queue")
 //OFPAT11_ACTION(OFPAT11_SET_NW_TTL,   ofp11_action_nw_ttl, 0, "set_nw_ttl")
 OFPAT11_ACTION(OFPAT11_DEC_NW_TTL,   ofp_action_header,   0, NULL)
 OFPAT11_ACTION(OFPAT12_SET_FIELD,    ofp12_action_set_field, 1, "set_field")
+OFPAT11_ACTION(OFPAT11_GROUP,        ofp11_action_group,   0, "group")
 
 #ifndef NXAST_ACTION
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index 39c81be..b97dbcf 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -224,6 +224,7 @@ struct ofputil_flow_mod {
     uint16_t hard_timeout;
     uint32_t buffer_id;
     ofp_port_t out_port;
+    uint32_t out_group;
     uint16_t flags;
     struct ofpact *ofpacts;     /* Series of "struct ofpact"s. */
     size_t ofpacts_len;         /* Length of ofpacts, in bytes. */
@@ -246,6 +247,7 @@ struct ofputil_flow_stats_request {
     ovs_be64 cookie;
     ovs_be64 cookie_mask;
     ofp_port_t out_port;
+    uint32_t out_group;
     uint8_t table_id;
 };
 
@@ -818,4 +820,101 @@ 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);
 
+/* Bucket for use in groups. */
+struct ofputil_bucket {
+    struct list list_node;
+    uint16_t weight;            /* Relative weight of bucket. Only defined for
+                                 * select groups. */
+    ofp_port_t watch_port;      /* Port whose state affects whether this bucket
+                                 * is live. Only required for fast failover
+                                 * groups. */
+    uint32_t watch_group;       /* Group whose state affects whether this
+                                 * bucket is live. Only required for fast
+                                 * failover groups. */
+    struct ofpact *ofpacts;     /* Series of "struct ofpact"s. */
+    size_t ofpacts_len;         /* Length of ofpacts, in bytes. */
+
+    struct list* actions;
+};
+
+struct ofputil_group_mod {
+    uint16_t command;             /* One of OFPGC11_*. */
+    uint8_t type;                 /* One of OFPGT11_*. */
+    uint32_t group_id;            /* Group identifier. */
+    struct list buckets;          /* Buckets. */
+};
+
+struct bucket_counter {
+    uint64_t packet_count;   /* Number of packets processed by bucket. */
+    uint64_t byte_count;     /* Number of bytes processed by bucket. */
+};
+
+/* Group stats reply, independent of protocol. */
+struct ofputil_group_stats {
+    uint32_t group_id;    /* Group identifier. */
+    uint32_t ref_count;
+    uint64_t packet_count;      /* Packet count, UINT64_MAX if unknown. */
+    uint64_t byte_count;        /* Byte count, UINT64_MAX if unknown. */
+    uint32_t duration_sec;
+    uint32_t duration_nsec;
+    uint32_t n_buckets;
+    struct bucket_counter bucket_stats[16];
+};
+
+/* Group features reply, independent of protocol. */
+struct ofputil_group_features {
+    uint32_t  types;           /* Bitmap of OFPGT_* values supported. */
+    uint32_t  capabilities;    /* Bitmap of OFPGFC12_* capability supported. */
+    uint32_t  max_groups[4];   /* Maximum number of groups for each type. */
+    uint32_t  actions[4];      /* Bitmaps of OFPAT_* that are supported. */
+};
+
+/* Group desc reply, independent of protocol. */
+struct ofputil_group_desc {
+    uint16_t length;            /* Length of this entry. */
+    uint8_t type;               /* One of OFPGT_*. */
+    uint8_t pad;                /* Pad to 64 bits. */
+    uint32_t group_id;          /* Group identifier. */
+    struct list buckets;        /* Buckets. */
+};
+
+struct ofpbuf *
+ofputil_encode_group_stats_request(enum ofp_version ofp_version, uint32_t group_id);
+enum ofperr
+ofputil_decode_group_stats_request(const struct ofp_header *request,
+                                  uint32_t *group_id);
+void
+ofputil_append_group_stats(struct list *replies,
+                         const struct ofputil_group_stats *ogs);
+struct ofpbuf *
+ofputil_encode_group_features_request(enum ofp_version ofp_version);
+struct ofpbuf *
+ofputil_encode_group_features_reply(const struct ofputil_group_features *features,
+                                 const struct ofp_header *request);
+enum ofperr
+ofputil_decode_group_features_reply(const struct ofp_header *oh,
+                               struct ofputil_group_features *features,
+                               struct ofpbuf *b);
+struct ofpbuf *
+ofputil_encode_group_mod(enum ofp_version ofp_version, const struct ofputil_group_mod *gm);
+
+enum ofperr
+ofputil_decode_group_mod(const struct ofp_header *oh,
+                        struct ofputil_group_mod *gm);
+
+int
+ofputil_decode_group_stats_reply(uint32_t raw,
+                               struct ofputil_group_stats *ogs,
+                               struct ofpbuf *b);
+
+int
+ofputil_decode_group_desc_reply(struct ofputil_group_desc *ogd,
+                               struct ofpbuf *b);
+
+void
+ofputil_append_group_desc_reply(const struct ofputil_group_desc *gds,
+                                struct list* buckets,
+                                struct list *replies);
+struct ofpbuf *
+ofputil_encode_group_desc_request(enum ofp_version ofp_version);
 #endif /* ofp-util.h */
diff --git a/lib/rconn.c b/lib/rconn.c
index 3e38c7d..ed4f26b 100644
--- a/lib/rconn.c
+++ b/lib/rconn.c
@@ -1152,6 +1152,7 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_PORT_STATUS:
     case OFPTYPE_PACKET_OUT:
     case OFPTYPE_FLOW_MOD:
+    case OFPTYPE_GROUP_MOD:
     case OFPTYPE_PORT_MOD:
     case OFPTYPE_METER_MOD:
     case OFPTYPE_BARRIER_REQUEST:
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 78617a2..5b27b26 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1530,6 +1530,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
                                 ofpact_get_OUTPUT(a)->max_len, true);
             break;
 
+        case OFPACT_GROUP:
+            /* XXX not yet implemented */
+            break;
+
         case OFPACT_CONTROLLER:
             controller = ofpact_get_CONTROLLER(a);
             execute_controller_action(ctx, controller->max_len,
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 4293824..113dfa3 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -6834,4 +6834,11 @@ const struct ofproto_class ofproto_dpif_class = {
     NULL,                       /* meter_set */
     NULL,                       /* meter_get */
     NULL,                       /* meter_del */
+    NULL,                       /* group_alloc */
+    NULL,                       /* group_dealloc */
+    NULL,                       /* group_construct */
+    NULL,                       /* group_modify */
+    NULL,                       /* group_destruct */
+    NULL,                       /* group_get_stats */
+    NULL,                       /* group_get_ref_cnt */
 };
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 41d106a..ca9056d 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -108,6 +108,12 @@ 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. */
+
+    /* Groups */
+    struct hmap groups;          /* Contains "struct ofgroup"s. */
+    struct ofputil_group_features ogf;
+    uint32_t alloc_groups[4];     /* Last allocated group number. */
+
 };
 
 void ofproto_init_tables(struct ofproto *, int n_tables);
@@ -269,9 +275,27 @@ void ofoperation_complete(struct ofoperation *, enum ofperr);
 struct rule *ofoperation_get_victim(struct ofoperation *);
 
 bool ofoperation_has_out_port(const struct ofoperation *, ofp_port_t out_port);
+bool ofproto_rule_has_out_group(const struct rule *, uint32_t group_id);
 
 bool ofproto_rule_is_hidden(const struct rule *);
 
+/* A group within a "struct ofproto".
+ *
+ * With few exceptions, ofproto implementations may look at these fields but
+ * should not modify them. */
+struct ofgroup {
+    struct hmap_node hmap_node; /* In struct ofproto's "group" hmap. */
+    struct ofproto *ofproto;    /* The ofproto that contains this group. */
+    uint32_t group_id;
+    uint8_t type;
+
+    long long int created;       /* Creation time. */
+    long long int modified;      /* Time of last modification. */
+
+    struct list buckets;
+    uint32_t n_buckets;
+};
+
 /* ofproto class structure, to be defined by each ofproto implementation.
  *
  *
@@ -1378,6 +1402,17 @@ struct ofproto_class {
     /* Deletes a meter, making the 'ofproto_meter_id' invalid for any
      * further calls. */
     void (*meter_del)(struct ofproto *, ofproto_meter_id);
+
+    struct ofgroup *(*group_alloc)(void);
+    void (*group_dealloc)(struct ofgroup *ofgroup);
+    int (*group_construct)(struct ofgroup *ofgroup);
+    int (*group_modify)(struct ofgroup *ofgroup, struct ofgroup *victim_);
+    int (*group_destruct)(struct ofgroup *ofgroup);
+    /* Get group stats */
+    int (*group_get_stats)(const struct ofgroup *ofgroup,
+                          struct ofputil_group_stats *stats);
+    int (*group_get_ref_cnt)(const struct ofgroup *ofgroup,
+                          uint32_t *ref_cnt);
 };
 
 extern const struct ofproto_class ofproto_dpif_class;
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 83b3472..bdb699f 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -201,6 +201,8 @@ static enum ofperr add_flow(struct ofproto *, struct ofconn *,
                             const struct ofp_header *);
 static void delete_flow__(struct rule *, struct ofopgroup *,
                           enum ofp_flow_removed_reason);
+static enum ofperr add_group(struct ofproto *, struct ofputil_group_mod *);
+static enum ofperr delete_group__(struct ofproto *, uint32_t group_id);
 static bool handle_openflow(struct ofconn *, const struct ofpbuf *);
 static enum ofperr handle_flow_mod__(struct ofproto *, struct ofconn *,
                                      struct ofputil_flow_mod *,
@@ -441,6 +443,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     ofproto->vlan_bitmap = NULL;
     ofproto->vlans_changed = false;
     ofproto->min_mtu = INT_MAX;
+    hmap_init(&ofproto->groups);
 
     error = ofproto->ofproto_class->construct(ofproto);
     if (error) {
@@ -2188,6 +2191,14 @@ ofproto_rule_has_out_port(const struct rule *rule, ofp_port_t port)
             || ofpacts_output_to_port(rule->ofpacts, rule->ofpacts_len, port));
 }
 
+/* Returns true if 'rule' has group and equals group_id. */
+bool
+ofproto_rule_has_out_group(const struct rule *rule, uint32_t group_id)
+{
+    return (group_id == OFPG11_ANY
+            || ofpacts_output_to_group(rule->ofpacts, rule->ofpacts_len, group_id));
+}
+
 /* Returns true if a rule related to 'op' has an OpenFlow OFPAT_OUTPUT or
  * OFPAT_ENQUEUE action that outputs to 'out_port'. */
 bool
@@ -2831,7 +2842,8 @@ static enum ofperr
 collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
                     const struct match *match,
                     ovs_be64 cookie, ovs_be64 cookie_mask,
-                    ofp_port_t out_port, struct list *rules)
+                    ofp_port_t out_port, uint32_t out_group,
+                    struct list *rules)
 {
     struct oftable *table;
     struct cls_rule cr;
@@ -2882,6 +2894,7 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
             }
             if (!ofproto_rule_is_hidden(rule)
                 && ofproto_rule_has_out_port(rule, out_port)
+                && ofproto_rule_has_out_group(rule, out_group)
                     && !((rule->flow_cookie ^ cookie) & cookie_mask)) {
                 list_push_back(rules, &rule->ofproto_node);
             }
@@ -2908,7 +2921,8 @@ static enum ofperr
 collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
                      const struct match *match, unsigned int priority,
                      ovs_be64 cookie, ovs_be64 cookie_mask,
-                     ofp_port_t out_port, struct list *rules)
+                     ofp_port_t out_port, uint32_t out_group,
+                     struct list *rules)
 {
     struct oftable *table;
     struct cls_rule cr;
@@ -2959,6 +2973,7 @@ collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
             }
             if (!ofproto_rule_is_hidden(rule)
                 && ofproto_rule_has_out_port(rule, out_port)
+                && ofproto_rule_has_out_group(rule, out_group)
                     && !((rule->flow_cookie ^ cookie) & cookie_mask)) {
                 list_push_back(rules, &rule->ofproto_node);
             }
@@ -2998,7 +3013,7 @@ handle_flow_stats_request(struct ofconn *ofconn,
 
     error = collect_rules_loose(ofproto, fsr.table_id, &fsr.match,
                                 fsr.cookie, fsr.cookie_mask,
-                                fsr.out_port, &rules);
+                                fsr.out_port, fsr.out_group, &rules);
     if (error) {
         return error;
     }
@@ -3120,7 +3135,7 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
 
     error = collect_rules_loose(ofproto, request.table_id, &request.match,
                                 request.cookie, request.cookie_mask,
-                                request.out_port, &rules);
+                                request.out_port, request.out_group, &rules);
     if (error) {
         return error;
     }
@@ -3520,7 +3535,7 @@ modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
 
     error = collect_rules_loose(ofproto, fm->table_id, &fm->match,
                                 fm->cookie, fm->cookie_mask,
-                                OFPP_ANY, &rules);
+                                OFPP_ANY, OFPG11_ANY, &rules);
     if (error) {
         return error;
     } else if (list_is_empty(&rules)) {
@@ -3545,8 +3560,7 @@ modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
 
     error = collect_rules_strict(ofproto, fm->table_id, &fm->match,
                                  fm->priority, fm->cookie, fm->cookie_mask,
-                                 OFPP_ANY, &rules);
-
+                                 OFPP_ANY, OFPG11_ANY, &rules);
     if (error) {
         return error;
     } else if (list_is_empty(&rules)) {
@@ -3604,7 +3618,7 @@ delete_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
 
     error = collect_rules_loose(ofproto, fm->table_id, &fm->match,
                                 fm->cookie, fm->cookie_mask,
-                                fm->out_port, &rules);
+                                fm->out_port, fm->out_group, &rules);
     return (error ? error
             : !list_is_empty(&rules) ? delete_flows__(ofproto, ofconn, request,
                                                       &rules, OFPRR_DELETE)
@@ -3622,7 +3636,7 @@ delete_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
 
     error = collect_rules_strict(ofproto, fm->table_id, &fm->match,
                                  fm->priority, fm->cookie, fm->cookie_mask,
-                                 fm->out_port, &rules);
+                                 fm->out_port, fm->out_group, &rules);
     return (error ? error
             : list_is_singleton(&rules) ? delete_flows__(ofproto, ofconn,
                                                          request, &rules,
@@ -3683,8 +3697,8 @@ ofproto_rule_expire(struct rule *rule, uint8_t reason)
     struct ofproto *ofproto = rule->ofproto;
     struct ofopgroup *group;
 
-    ovs_assert(reason == OFPRR_HARD_TIMEOUT || reason == OFPRR_IDLE_TIMEOUT);
-
+    ovs_assert(reason == OFPRR_HARD_TIMEOUT || reason == OFPRR_IDLE_TIMEOUT
+               || reason == OFPRR_DELETE || reason == OFPRR_GROUP_DELETE);
     ofproto_rule_send_removed(rule, reason);
 
     group = ofopgroup_create_unattached(ofproto);
@@ -4494,6 +4508,406 @@ handle_meter_request(struct ofconn *ofconn, const struct ofp_header *request,
     return 0;
 }
 
+static struct ofgroup *
+ofproto_group_lookup(const struct ofproto *ofproto,
+                           uint32_t group_id)
+{
+    struct ofgroup *group;
+
+    HMAP_FOR_EACH_IN_BUCKET (group, hmap_node,
+                             hash_int(group_id, 0), &ofproto->groups) {
+        if (group->group_id == group_id) {
+            return group;
+        }
+    }
+
+    return NULL;
+}
+
+static void
+append_group_stats(struct ofgroup *group, struct list *replies)
+{
+    struct ofputil_group_stats ogs;
+    struct ofproto *ofproto = group->ofproto;
+    long long int now = time_msec();
+
+    memset(&ogs, 0, sizeof(ogs));
+
+    /* Intentionally ignore return value, since errors will set
+     * 'stats' to all-1s, which is correct for OpenFlow */
+    if (ofproto->ofproto_class->group_get_stats) {
+        ofproto->ofproto_class->group_get_stats(group, &ogs);
+    }
+    if (ofproto->ofproto_class->group_get_ref_cnt) {
+        ofproto->ofproto_class->group_get_ref_cnt(group, &ogs.ref_count);
+    }
+
+    calc_duration(group->created, now, &ogs.duration_sec, &ogs.duration_nsec);
+
+    ofputil_append_group_stats(replies, &ogs);
+}
+
+static enum ofperr
+handle_group_stats_request(struct ofconn *ofconn,
+                           const struct ofp_header *request)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    struct list replies;
+    enum ofperr error;
+    struct ofgroup *group;
+    uint32_t group_id;
+
+    error = ofputil_decode_group_stats_request(request, &group_id);
+    if (error) {
+        return error;
+    }
+
+    ofpmp_init(&replies, request);
+
+    if (group_id == OFPG_ALL){
+        HMAP_FOR_EACH (group, hmap_node, &ofproto->groups) {
+            append_group_stats(group, &replies);
+        }
+    } else {
+        group = ofproto_group_lookup(ofproto, group_id);
+        if (group) {
+            append_group_stats(group, &replies);
+        }
+    }
+
+    ofconn_send_replies(ofconn, &replies);
+
+    return 0;
+}
+
+static enum ofperr
+handle_group_desc_stats_request(struct ofconn *ofconn,
+                                const struct ofp_header *request)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    struct list replies;
+    struct ofputil_group_desc gds;
+    struct ofgroup *group;
+
+    ofpmp_init(&replies, request);
+
+    HMAP_FOR_EACH (group, hmap_node, &ofproto->groups) {
+        gds.group_id = group->group_id;
+        gds.length = 0;
+        gds.type = group->type;
+        ofputil_append_group_desc_reply(&gds, &group->buckets, &replies);
+    }
+
+    ofconn_send_replies(ofconn, &replies);
+
+    return 0;
+}
+
+static enum ofperr
+handle_group_features_stats_request(struct ofconn *ofconn,
+                                    const struct ofp_header *request)
+{
+    struct ofproto *p = ofconn_get_ofproto(ofconn);
+     struct ofpbuf *msg;
+
+    msg = ofputil_encode_group_features_reply(&p->ogf, request);
+
+    if (msg) {
+        ofconn_send_reply(ofconn, msg);
+    }
+
+    return 0;
+}
+
+/* Checks whether 'gm' is a valid group.  Returns
+ * 0 if 'gm' is OK, otherwise an OpenFlow error code.  */
+static enum ofperr
+ofproto_group_check_valid(const struct ofproto *ofproto OVS_UNUSED,
+                          const struct ofputil_group_mod *gm)
+{
+    struct ofgroup *ofgroup;
+
+    if (gm->group_id > OFPG_MAX) {
+        return OFPERR_OFPGMFC_INVALID_GROUP;
+    }
+
+    ofgroup = ofproto_group_lookup(ofproto, gm->group_id);
+    if (ofgroup) {
+        return OFPERR_OFPGMFC_GROUP_EXISTS;
+    }
+
+    return 0;
+}
+
+static int
+group_map_group_buckets(struct ofputil_group_mod *gm, struct ofgroup *ofgroup)
+{
+    struct list *list_node;
+
+    list_init(&ofgroup->buckets);
+
+    ofgroup->n_buckets = 0;
+    while(!list_is_empty(&gm->buckets)) {
+        list_node = list_pop_front(&gm->buckets);
+        list_push_back(&ofgroup->buckets, list_node);
+        ofgroup->n_buckets ++;
+    }
+
+    return 0;
+}
+
+static int
+group_move_group_buckets(struct ofgroup *from, struct ofgroup *to)
+{
+    struct list *list_node;
+
+    list_init(&to->buckets);
+
+    to->n_buckets = 0;
+    while(!list_is_empty(&from->buckets)) {
+        list_node = list_pop_front(&from->buckets);
+        list_push_back(&to->buckets, list_node);
+        to->n_buckets ++;
+    }
+
+    return 0;
+}
+
+static int
+group_free_group_buckets(struct ofgroup *ofgroup)
+{
+    struct list *list_node;
+    struct ofputil_bucket *bucket;
+
+    while(!list_is_empty(&ofgroup->buckets)) {
+        list_node = list_pop_front(&ofgroup->buckets);
+        bucket = (struct ofputil_bucket *)list_node;
+        free(bucket->ofpacts);
+        free(bucket);
+    }
+
+    return 0;
+}
+
+/* Implements OFPGC11_ADD
+ * in which no matching flow already exists in the flow table.
+ *
+ * Adds the flow specified by 'ofm', which is followed by 'n_actions'
+ * ofp_actions, to the ofproto's flow table.  Returns 0 on success, an OpenFlow
+ * error code on failure, or OFPROTO_POSTPONE if the operation cannot be
+ * initiated now but may be retried later.
+ *
+ * Upon successful return, takes ownership of 'fm->ofpacts'.  On failure,
+ * ownership remains with the caller.
+ *
+ * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
+ * if any. */
+static enum ofperr
+add_group(struct ofproto *ofproto, struct ofputil_group_mod *gm)
+{
+    int error;
+    struct ofgroup *ofgroup;
+
+    error = ofproto_group_check_valid(ofproto, gm);
+    if (error) {
+        return error;
+    }
+
+    if (gm->type > OFPGT11_FF) {
+        return OFPERR_OFPGMFC_BAD_TYPE;
+    }
+
+    if (ofproto->alloc_groups[gm->type] >=  ofproto->ogf.max_groups[gm->type]) {
+        return OFPERR_OFPGMFC_OUT_OF_GROUPS;
+    }
+
+    /* Allocate new group and initialize it. */
+    ofgroup = ofproto->ofproto_class->group_alloc();
+    if (!ofgroup) {
+        VLOG_WARN_RL(&rl, "%s: failed to create group (%s)",
+                     ofproto->name, strerror(error));
+        return OFPERR_OFPGMFC_OUT_OF_GROUPS;
+    }
+
+    ofgroup->ofproto  = ofproto;
+    ofgroup->group_id = gm->group_id;
+    ofgroup->type     = gm->type;
+    ofgroup->created = ofgroup->modified = time_msec();
+
+    group_map_group_buckets(gm, ofgroup);
+
+    hmap_insert(&ofproto->groups, &ofgroup->hmap_node, hash_int(ofgroup->group_id, 0));
+    ofproto->alloc_groups[ofgroup->type]++;
+
+    /* Insert new group. */
+    error = ofproto->ofproto_class->group_construct(ofgroup);
+    if (error) {
+
+        group_free_group_buckets(ofgroup);
+
+        hmap_remove(&ofproto->groups, &ofgroup->hmap_node);
+        ofproto->ofproto_class->group_dealloc(ofgroup);
+        ofproto->alloc_groups[gm->type]--;
+    }
+
+    return error;
+}
+
+/* Implements OFPFC_MODIFY.  Returns 0 on success or an OpenFlow error code on
+ * failure.
+ *
+ * 'ofconn' is used to retrieve the packet buffer specified in fm->buffer_id,
+ * if any. */
+static enum ofperr
+modify_group(struct ofproto *ofproto, struct ofputil_group_mod *gm)
+{
+    struct ofgroup *ofgroup;
+    struct ofgroup *victim;
+    int error = 0;
+
+    if (gm->group_id > OFPG_MAX) {
+        return OFPERR_OFPGMFC_INVALID_GROUP;
+    }
+
+    if (gm->type > OFPGT11_FF) {
+        return OFPERR_OFPGMFC_BAD_TYPE;
+    }
+
+    ofgroup = ofproto_group_lookup(ofproto, gm->group_id);
+    if (!ofgroup) {
+        return OFPERR_OFPGMFC_UNKNOWN_GROUP;
+    }
+
+    if (ofgroup->type != gm->type) {
+        if (ofproto->alloc_groups[gm->type] >=  ofproto->ogf.max_groups[gm->type]) {
+            return OFPERR_OFPGMFC_OUT_OF_GROUPS;
+        }
+    }
+
+    victim = ofproto->ofproto_class->group_alloc();
+    if (!victim) {
+        VLOG_WARN_RL(&rl, "%s: failed to create group (%s)",
+                     ofproto->name, strerror(error));
+        return OFPERR_OFPGMFC_OUT_OF_GROUPS;
+    }
+    memcpy(victim, ofgroup, sizeof(struct ofgroup));
+    group_move_group_buckets(ofgroup, victim);
+
+    ofgroup->type = gm->type;
+    group_map_group_buckets(gm, ofgroup);
+
+    error = ofproto->ofproto_class->group_modify(ofgroup, victim);
+    if (error) {
+        group_free_group_buckets(ofgroup);
+
+        memcpy(ofgroup, victim, sizeof(struct ofgroup));
+        group_move_group_buckets(victim, ofgroup);
+    } else {
+        group_free_group_buckets(victim);
+        ofproto->alloc_groups[victim->type]--;
+        ofproto->alloc_groups[ofgroup->type]++;
+        ofgroup->modified = time_msec();
+    }
+    ofproto->ofproto_class->group_dealloc(victim);
+
+    return error;
+}
+
+static enum ofperr
+delete_group__(struct ofproto *ofproto, uint32_t group_id)
+{
+    enum ofperr error = 0;
+    struct ofgroup *ofgroup;
+
+    ofgroup = ofproto_group_lookup(ofproto, group_id);
+    if (!ofgroup) {
+        return 0;
+    }
+
+    /* Delete group. */
+    error = ofproto->ofproto_class->group_destruct(ofgroup);
+    if (error) {
+        return error;
+    }
+
+    group_free_group_buckets(ofgroup);
+
+    hmap_remove(&ofproto->groups, &ofgroup->hmap_node);
+    ofproto->alloc_groups[ofgroup->type]--;
+    ofproto->ofproto_class->group_dealloc(ofgroup);
+
+    return error;
+}
+
+/* Implements OFPGC_DELETE. */
+static enum ofperr
+delete_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm)
+{
+    if (gm->group_id == OFPG_ALL) {
+        struct ofgroup *ofgroup, *next_ofgroup;
+
+        /* Remove indirect groups first, to allow other groups to be removed
+         * successfully. */
+        HMAP_FOR_EACH_SAFE (ofgroup, next_ofgroup, hmap_node,
+                            &ofproto->groups) {
+            if (ofgroup->type == OFPGT11_INDIRECT) {
+                delete_group__(ofproto, ofgroup->group_id);
+            }
+        }
+        HMAP_FOR_EACH_SAFE (ofgroup, next_ofgroup, hmap_node,
+                            &ofproto->groups) {
+            delete_group__(ofproto, ofgroup->group_id);
+        }
+    } else {
+        return delete_group__(ofproto, gm->group_id);
+    }
+
+    return 0;
+}
+
+static enum ofperr
+handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    struct ofputil_group_mod gm;
+    enum ofperr error = 0;
+
+    error = reject_slave_controller(ofconn);
+    if (error) {
+        goto exit;
+    }
+
+    error = ofputil_decode_group_mod(oh, &gm);
+    if (error) {
+        goto exit;
+    }
+
+    switch (gm.command) {
+    case OFPGC11_ADD:
+        error = add_group(ofproto, &gm);
+        break;
+
+    case OFPGC11_MODIFY:
+        error = modify_group(ofproto, &gm);
+        break;
+
+    case OFPGC11_DELETE:
+        error = delete_group(ofproto, &gm);
+        break;
+
+    default:
+        if (gm.command > OFPGC11_DELETE) {
+            VLOG_WARN_RL(&rl, "%s: Invalid group_mod command type %d",
+                         ofproto->name, gm.command);
+        }
+        error = OFPERR_OFPGMFC_BAD_COMMAND;
+        break;
+    }
+
+exit:
+    return error;
+}
+
 static enum ofperr
 handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
 {
@@ -4529,6 +4943,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_MOD:
         return handle_flow_mod(ofconn, oh);
 
+    case OFPTYPE_GROUP_MOD:
+        return handle_group_mod(ofconn, oh);
+
     case OFPTYPE_METER_MOD:
         return handle_meter_mod(ofconn, oh);
 
@@ -4597,12 +5014,18 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_METER_FEATURES_STATS_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_GROUP_STATS_REQUEST:
+        return handle_group_stats_request(ofconn, oh);
+
     case OFPTYPE_GROUP_DESC_STATS_REQUEST:
+        return handle_group_desc_stats_request(ofconn, oh);
+
     case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
+        return handle_group_features_stats_request(ofconn, oh);
+
+        /* FIXME: Change the following once they are implemented: */
+    case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
+    case OFPTYPE_GET_ASYNC_REQUEST:
     case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
         return OFPERR_OFPBRC_BAD_TYPE;
 
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 2d6872b..be205f8 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-group SWITCH GROUP      add group described by GROUP\n"
+           "  add-group SWITCH FILE       add group from FILE\n"
+           "  mod-group SWITCH GROUP      modify specific group\n"
+           "  del-groups SWITCH [GROUP]   delete matching GROUPs\n"
+           "  dump-group-fea SWITCH       print group features\n"
+           "  dump-groups SWITCH          print group description\n"
+           "  dump-group-stats SWITCH [GROUP]  print group statistics\n"
            "\nFor OpenFlow switches and controllers:\n"
            "  probe TARGET                probe whether TARGET is up\n"
            "  ping TARGET [N]             latency of N-byte echos\n"
@@ -1765,6 +1772,133 @@ ofctl_benchmark(int argc OVS_UNUSED, char *argv[])
 }
 
 static void
+ofctl_group_mod__(const char *remote, struct ofputil_group_mod *gms,
+                 size_t n_gms)
+{
+    struct ofputil_group_mod *gm;
+    struct ofpbuf *request;
+
+    struct vconn *vconn;
+    size_t i;
+
+    open_vconn(remote, &vconn);
+
+    for (i = 0; i < n_gms; i++) {
+        gm = &gms[i];
+        request = ofputil_encode_group_mod(vconn_get_version(vconn), gm);
+        if (request) {
+            transact_noreply(vconn, request);
+        }
+    }
+
+    vconn_close(vconn);
+
+}
+
+
+static void
+ofctl_group_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
+{
+    struct ofputil_group_mod *gms = NULL;
+    size_t n_gms = 0;
+
+    parse_ofp_group_mod_file(argv[2], command, &gms, &n_gms);
+    ofctl_group_mod__(argv[1], gms, n_gms);
+    free(gms);
+
+}
+
+static void
+ofctl_group_mod(int argc, char *argv[], uint16_t command)
+{
+    if (argc > 2 && !strcmp(argv[2], "-")) {
+        ofctl_group_mod_file(argc, argv, command);
+    } else {
+        struct ofputil_group_mod gm;
+        memset(&gm, 0, sizeof(gm));
+        parse_ofp_group_mod_str(&gm, argc > 2 ? argv[2] : "", command, false);
+        ofctl_group_mod__(argv[1], &gm, 1);
+    }
+}
+
+static void
+ofctl_add_group(int argc, char *argv[])
+{
+    ofctl_group_mod(argc, argv, OFPGC11_ADD);
+}
+
+static void
+ofctl_add_groups(int argc, char *argv[])
+{
+    ofctl_group_mod_file(argc, argv, OFPGC11_ADD);
+}
+
+static void
+ofctl_mod_group(int argc, char *argv[])
+{
+    ofctl_group_mod(argc, argv, OFPGC11_MODIFY);
+}
+
+static void
+ofctl_del_groups(int argc, char *argv[])
+{
+    ofctl_group_mod(argc, argv, OFPGC11_DELETE);
+}
+
+static void
+ofctl_dump_group_stats(int argc, char *argv[])
+{
+    struct ofpbuf *request;
+    struct vconn *vconn;
+    uint32_t group_id;
+    struct ofputil_group_mod gm;
+
+    memset(&gm, 0, sizeof(gm));
+
+    parse_ofp_group_mod_str(&gm, argc > 2 ? argv[2] : "", OFPGC11_DELETE, false);
+    group_id = gm.group_id;
+
+    open_vconn(argv[1], &vconn);
+    request = ofputil_encode_group_stats_request(vconn_get_version(vconn), group_id);
+    if (request) {
+        dump_stats_transaction(vconn, request);
+    }
+
+    vconn_close(vconn);
+}
+
+static void
+ofctl_dump_group_desc(int argc OVS_UNUSED, char *argv[])
+{
+    struct ofpbuf *request;
+    struct vconn *vconn;
+
+    open_vconn(argv[1], &vconn);
+
+    request = ofputil_encode_group_desc_request(vconn_get_version(vconn));
+    if (request) {
+        dump_stats_transaction(vconn, request);
+    }
+
+    vconn_close(vconn);
+}
+
+static void
+ofctl_dump_group_features(int argc OVS_UNUSED, char *argv[])
+{
+    struct ofpbuf *request;
+    struct vconn *vconn;
+
+    open_vconn(argv[1], &vconn);
+    request = ofputil_encode_group_features_request(vconn_get_version(vconn));
+    if (request) {
+        dump_stats_transaction(vconn, request);
+    }
+
+    vconn_close(vconn);
+}
+
+static void
 ofctl_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
     usage();
@@ -2893,6 +3027,14 @@ static const struct command all_commands[] = {
     { "probe", 1, 1, ofctl_probe },
     { "ping", 1, 2, ofctl_ping },
     { "benchmark", 3, 3, ofctl_benchmark },
+
+    { "add-group", 1, 2, ofctl_add_group },
+    { "add-groups", 1, 2, ofctl_add_groups },
+    { "mod-group", 1, 2, ofctl_mod_group },
+    { "del-groups", 1, 2, ofctl_del_groups },
+    { "dump-groups", 1, 1, ofctl_dump_group_desc },
+    { "dump-group-stats", 1, 2, ofctl_dump_group_stats },
+    { "dump-group-fea", 1, 1, ofctl_dump_group_features },
     { "help", 0, INT_MAX, ofctl_help },
 
     /* Undocumented commands for testing. */
-- 
1.7.2.5




More information about the dev mailing list