X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fofp-util.c;h=9edfe9ef03ea56403add367854530d9d499f301a;hb=57e8d2bdc174cb24e2ae474ea805c7b7308d68a5;hp=61fb74feb06ec9c4d65dc4c5c432e7739188da6b;hpb=db0b6c2913dfda237f07881b059ba267f3388600;p=sliver-openvswitch.git diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 61fb74feb..9edfe9ef0 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -1537,6 +1537,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)) { @@ -1578,6 +1580,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; raw_flags = ofm->flags; } else if (raw == OFPRAW_NXT_FLOW_MOD) { /* Nicira extended flow_mod. */ @@ -1608,6 +1611,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; raw_flags = nfm->flags; } else { NOT_REACHED(); @@ -2058,7 +2062,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 = raw_flags; ofputil_put_ofp11_match(msg, &fm->match, protocol); ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len, msg); @@ -2124,6 +2128,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); @@ -2144,9 +2149,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); @@ -2176,6 +2179,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; @@ -2242,7 +2246,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; ofputil_put_ofp11_match(msg, &fsr->match, protocol); @@ -2486,7 +2490,7 @@ unknown_to_zero(uint64_t count) /* Appends an OFPST_FLOW or NXST_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(). */ + * have been initialized with ofpmp_init(). */ void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, struct list *replies) @@ -3656,7 +3660,7 @@ ofputil_encode_port_status(const struct ofputil_port_status *ps, ofpmsg_update_length(b); return b; } - + /* ofputil_port_mod */ /* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in @@ -3738,7 +3742,66 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm, opm->advertise = netdev_port_features_to_ofp11(pm->advertise); break; } + default: + NOT_REACHED(); + } + + return b; +} + +/* ofputil_table_mod */ + +/* Decodes the OpenFlow "table mod" message in '*oh' into an abstract form in + * '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */ +enum ofperr +ofputil_decode_table_mod(const struct ofp_header *oh, + struct ofputil_table_mod *pm) +{ + enum ofpraw raw; + struct ofpbuf b; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + raw = ofpraw_pull_assert(&b); + + if (raw == OFPRAW_OFPT11_TABLE_MOD) { + const struct ofp11_table_mod *otm = b.data; + + pm->table_id = otm->table_id; + pm->config = ntohl(otm->config); + } else { + return OFPERR_OFPBRC_BAD_TYPE; + } + + return 0; +} + +/* Converts the abstract form of a "table mod" message in '*pm' into an OpenFlow + * message suitable for 'protocol', and returns that encoded form in a buffer + * owned by the caller. */ +struct ofpbuf * +ofputil_encode_table_mod(const struct ofputil_table_mod *pm, + enum ofputil_protocol protocol) +{ + enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol); + struct ofpbuf *b; + + switch (ofp_version) { + case OFP10_VERSION: { + ovs_fatal(0, "table mod needs OpenFlow 1.1 or later " + "(\'-O OpenFlow11\')"); + break; + } + case OFP11_VERSION: + case OFP12_VERSION: + case OFP13_VERSION: { + struct ofp11_table_mod *otm; + b = ofpraw_alloc(OFPRAW_OFPT11_TABLE_MOD, ofp_version, 0); + otm = ofpbuf_put_zeros(b, sizeof *otm); + otm->table_id = pm->table_id; + otm->config = htonl(pm->config); + break; + } default: NOT_REACHED(); } @@ -3947,6 +4010,19 @@ ofputil_put_ofp11_table_stats(const struct ofp12_table_stats *in, out->matched_count = in->matched_count; } +static void +ofputil_put_ofp12_table_stats(const struct ofp12_table_stats *in, + struct ofpbuf *buf) +{ + struct ofp12_table_stats *out = ofpbuf_put(buf, in, sizeof *in); + + /* Trim off OF1.3-only capabilities. */ + out->match &= htonll(OFPXMT12_MASK); + out->wildcards &= htonll(OFPXMT12_MASK); + out->write_setfields &= htonll(OFPXMT12_MASK); + out->apply_setfields &= htonll(OFPXMT12_MASK); +} + static void ofputil_put_ofp13_table_stats(const struct ofp12_table_stats *in, struct ofpbuf *buf) @@ -3972,31 +4048,27 @@ ofputil_encode_table_stats_reply(const struct ofp12_table_stats stats[], int n, reply = ofpraw_alloc_stats_reply(request, n * sizeof *stats); - switch ((enum ofp_version) request->version) { - case OFP10_VERSION: - for (i = 0; i < n; i++) { + for (i = 0; i < n; i++) { + switch ((enum ofp_version) request->version) { + case OFP10_VERSION: ofputil_put_ofp10_table_stats(&stats[i], reply); - } - break; + break; - case OFP11_VERSION: - for (i = 0; i < n; i++) { + case OFP11_VERSION: ofputil_put_ofp11_table_stats(&stats[i], reply); - } - break; + break; - case OFP12_VERSION: - ofpbuf_put(reply, stats, n * sizeof *stats); - break; + case OFP12_VERSION: + ofputil_put_ofp12_table_stats(&stats[i], reply); + break; - case OFP13_VERSION: - for (i = 0; i < n; i++) { + case OFP13_VERSION: ofputil_put_ofp13_table_stats(&stats[i], reply); - } - break; + break; - default: - NOT_REACHED(); + default: + NOT_REACHED(); + } } return reply; @@ -4576,6 +4648,65 @@ ofputil_port_to_string(ofp_port_t port, } } +/* Stores the group id represented by 's' into '*group_idp'. 's' may be an + * integer or, for reserved group IDs, the standard OpenFlow name for the group + * (either "ANY" or "ALL"). + * + * Returns true if successful, false if 's' is not a valid OpenFlow group ID or + * name. */ +bool +ofputil_group_from_string(const char *s, uint32_t *group_idp) +{ + if (!strcasecmp(s, "any")) { + *group_idp = OFPG11_ANY; + } else if (!strcasecmp(s, "all")) { + *group_idp = OFPG11_ALL; + } else if (!str_to_uint(s, 10, group_idp)) { + VLOG_WARN("%s is not a valid group ID. (Valid group IDs are " + "32-bit nonnegative integers or the keywords ANY or " + "ALL.)", s); + return false; + } + + return true; +} + +/* Appends to 's' a string representation of the OpenFlow group ID 'group_id'. + * Most groups' string representation is just the number, but for special + * groups, e.g. OFPG11_ALL, it is the name, e.g. "ALL". */ +void +ofputil_format_group(uint32_t group_id, struct ds *s) +{ + char name[MAX_GROUP_NAME_LEN]; + + ofputil_group_to_string(group_id, name, sizeof name); + ds_put_cstr(s, name); +} + + +/* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string + * representation of OpenFlow group ID 'group_id'. Most group are represented + * as just their number, but special groups, e.g. OFPG11_ALL, are represented + * by name, e.g. "ALL". */ +void +ofputil_group_to_string(uint32_t group_id, + char namebuf[MAX_GROUP_NAME_LEN + 1], size_t bufsize) +{ + switch (group_id) { + case OFPG11_ALL: + ovs_strlcpy(namebuf, "ALL", bufsize); + break; + + case OFPG11_ANY: + ovs_strlcpy(namebuf, "ANY", bufsize); + break; + + default: + snprintf(namebuf, bufsize, "%"PRIu32, group_id); + break; + } +} + /* Given a buffer 'b' that contains an array of OpenFlow ports of type * 'ofp_version', tries to pull the first element from the array. If * successful, initializes '*pp' with an abstract representation of the @@ -5193,6 +5324,543 @@ ofputil_decode_port_stats_request(const struct ofp_header *request, } } +/* Frees all of the "struct ofputil_bucket"s in the 'buckets' list. */ +void +ofputil_bucket_list_destroy(struct list *buckets) +{ + struct ofputil_bucket *bucket, *next_bucket; + + LIST_FOR_EACH_SAFE (bucket, next_bucket, list_node, buckets) { + list_remove(&bucket->list_node); + free(bucket->ofpacts); + free(bucket); + } +} + +/* Returns an OpenFlow group stats request for OpenFlow version 'ofp_version', + * that requests stats for group 'group_id'. (Use OFPG_ALL to request stats + * for all groups.) + * + * Group statistics include packet and byte counts for each group. */ +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-group-stats needs OpenFlow 1.1 or later " + "(\'-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; +} + +/* Returns an OpenFlow group description request for OpenFlow version + * 'ofp_version', that requests stats for group 'group_id'. (Use OFPG_ALL to + * request stats for all groups.) + * + * Group descriptions include the bucket and action configuration for each + * group. */ +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 needs OpenFlow 1.1 or later " + "(\'-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, + size_t base_len, struct list *replies) +{ + struct ofp11_bucket_counter *bc11; + struct ofp11_group_stats *gs11; + size_t length; + int i; + + length = base_len + sizeof(struct ofp11_bucket_counter) * ogs->n_buckets; + + gs11 = ofpmp_append(replies, length); + memset(gs11, 0, base_len); + gs11->length = htons(length); + gs11->group_id = htonl(ogs->group_id); + gs11->ref_count = htonl(ogs->ref_count); + gs11->packet_count = htonll(ogs->packet_count); + gs11->byte_count = htonll(ogs->byte_count); + + bc11 = (void *) (((uint8_t *) gs11) + base_len); + for (i = 0; i < ogs->n_buckets; i++) { + const struct bucket_counter *obc = &ogs->bucket_stats[i]; + + bc11[i].packet_count = htonll(obc->packet_count); + bc11[i].byte_count = htonll(obc->byte_count); + } + + return gs11; +} + +static void +ofputil_append_of13_group_stats(const struct ofputil_group_stats *ogs, + struct list *replies) +{ + struct ofp13_group_stats *gs13; + + gs13 = ofputil_group_stats_to_ofp11(ogs, sizeof *gs13, replies); + gs13->duration_sec = htonl(ogs->duration_sec); + gs13->duration_nsec = htonl(ogs->duration_nsec); +} + +/* Encodes 'ogs' properly for the format of the list of group statistics + * replies already begun in 'replies' and appends it to the list. 'replies' + * must have originally been initialized with ofpmp_init(). */ +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; + + switch ((enum ofp_version)oh->version) { + case OFP11_VERSION: + case OFP12_VERSION: + ofputil_group_stats_to_ofp11(ogs, sizeof(struct ofp11_group_stats), + replies); + break; + + case OFP13_VERSION: + ofputil_append_of13_group_stats(ogs, replies); + break; + + case OFP10_VERSION: + default: + NOT_REACHED(); + } +} + +/* Returns an OpenFlow group features request for OpenFlow version + * 'ofp_version'. */ +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-group-features needs OpenFlow 1.2 or later " + "(\'-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; +} + +/* Returns a OpenFlow message that encodes 'features' properly as a reply to + * group features request 'request'. */ +struct ofpbuf * +ofputil_encode_group_features_reply( + const struct ofputil_group_features *features, + const struct ofp_header *request) +{ + struct ofp12_group_features_stats *ogf; + struct ofpbuf *reply; + + reply = ofpraw_alloc_xid(OFPRAW_OFPST12_GROUP_FEATURES_REPLY, + 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; +} + +/* Decodes group features reply 'oh' into 'features'. */ +void +ofputil_decode_group_features_reply(const struct ofp_header *oh, + struct ofputil_group_features *features) +{ + const struct ofp12_group_features_stats *ogf = ofpmsg_body(oh); + + 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]); +} + +/* 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) +{ + const struct ofp11_group_stats_request *gsr11 = ofpmsg_body(request); + *group_id = ntohl(gsr11->group_id); + return 0; +} + +/* Converts a group stats reply in 'msg' into an abstract ofputil_group_stats + * in 'gs'. Assigns freshly allocated memory to gs->bucket_stats for the + * caller to eventually free. + * + * Multiple group stats replies can be packed into a single OpenFlow message. + * Calling this function multiple times for a single 'msg' iterates through the + * replies. The caller must initially leave 'msg''s layer pointers null and + * not modify them between calls. + * + * Returns 0 if successful, EOF if no replies were left in this 'msg', + * otherwise a positive errno value. */ +int +ofputil_decode_group_stats_reply(struct ofpbuf *msg, + struct ofputil_group_stats *gs) +{ + struct ofp11_bucket_counter *obc; + struct ofp11_group_stats *ogs11; + enum ofpraw raw; + enum ofperr error; + size_t base_len; + size_t length; + size_t i; + + gs->bucket_stats = NULL; + error = (msg->l2 + ? ofpraw_decode(&raw, msg->l2) + : ofpraw_pull(&raw, msg)); + if (error) { + return error; + } + + if (!msg->size) { + return EOF; + } + + if (raw == OFPRAW_OFPST11_GROUP_REPLY) { + base_len = sizeof *ogs11; + ogs11 = ofpbuf_try_pull(msg, sizeof *ogs11); + gs->duration_sec = gs->duration_nsec = UINT32_MAX; + } else if (raw == OFPRAW_OFPST13_GROUP_REPLY) { + struct ofp13_group_stats *ogs13; + + base_len = sizeof *ogs13; + ogs13 = ofpbuf_try_pull(msg, sizeof *ogs13); + if (ogs13) { + ogs11 = &ogs13->gs; + gs->duration_sec = ntohl(ogs13->duration_sec); + gs->duration_nsec = ntohl(ogs13->duration_nsec); + } else { + ogs11 = NULL; + } + } else { + NOT_REACHED(); + } + + if (!ogs11) { + VLOG_WARN_RL(&bad_ofmsg_rl, "%s reply has %zu leftover bytes at end", + ofpraw_get_name(raw), msg->size); + return OFPERR_OFPBRC_BAD_LEN; + } + length = ntohs(ogs11->length); + if (length < sizeof base_len) { + VLOG_WARN_RL(&bad_ofmsg_rl, "%s reply claims invalid length %zu", + ofpraw_get_name(raw), length); + return OFPERR_OFPBRC_BAD_LEN; + } + + gs->group_id = ntohl(ogs11->group_id); + gs->ref_count = ntohl(ogs11->ref_count); + gs->packet_count = ntohll(ogs11->packet_count); + gs->byte_count = ntohll(ogs11->byte_count); + + gs->n_buckets = (length - base_len) / sizeof *obc; + obc = ofpbuf_try_pull(msg, gs->n_buckets * sizeof *obc); + if (!obc) { + VLOG_WARN_RL(&bad_ofmsg_rl, "%s reply has %zu leftover bytes at end", + ofpraw_get_name(raw), msg->size); + return OFPERR_OFPBRC_BAD_LEN; + } + + gs->bucket_stats = xmalloc(gs->n_buckets * sizeof *gs->bucket_stats); + for (i = 0; i < gs->n_buckets; i++) { + gs->bucket_stats[i].packet_count = ntohll(obc[i].packet_count); + gs->bucket_stats[i].byte_count = ntohll(obc[i].byte_count); + } + + return 0; +} + +/* Appends a group stats reply that contains the data in 'gds' to those already + * present in the list of ofpbufs in 'replies'. 'replies' should have been + * initialized with ofpmp_init(). */ +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)); + struct ofp11_group_desc_stats *ogds; + struct ofputil_bucket *bucket; + size_t start_ogds; + + start_ogds = reply->size; + ofpbuf_put_zeros(reply, sizeof *ogds); + LIST_FOR_EACH (bucket, list_node, buckets) { + struct ofp11_bucket *ob; + size_t start_ob; + + start_ob = reply->size; + ofpbuf_put_zeros(reply, sizeof *ob); + ofpacts_put_openflow11_actions(bucket->ofpacts, + bucket->ofpacts_len, reply); + + ob = ofpbuf_at_assert(reply, start_ob, sizeof *ob); + ob->len = htons(reply->size - start_ob); + ob->weight = htons(bucket->weight); + 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->group_id = htonl(gds->group_id); + + ofpmp_postappend(replies, start_ogds); +} + +static enum ofperr +ofputil_pull_buckets(struct ofpbuf *msg, size_t buckets_length, + struct list *buckets) +{ + struct ofp11_bucket *ob; + + list_init(buckets); + while (buckets_length > 0) { + struct ofputil_bucket *bucket; + struct ofpbuf ofpacts; + enum ofperr error; + size_t ob_len; + + ob = (buckets_length >= sizeof *ob + ? ofpbuf_try_pull(msg, sizeof *ob) + : NULL); + if (!ob) { + VLOG_WARN_RL(&bad_ofmsg_rl, "buckets end with %zu leftover bytes", + buckets_length); + } + + ob_len = ntohs(ob->len); + if (ob_len < sizeof *ob) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bucket length " + "%zu is not valid", ob_len); + return OFPERR_OFPGMFC_BAD_BUCKET; + } else if (ob_len > buckets_length) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bucket length " + "%zu exceeds remaining buckets data size %zu", + ob_len, buckets_length); + return OFPERR_OFPGMFC_BAD_BUCKET; + } + buckets_length -= ob_len; + + ofpbuf_init(&ofpacts, 0); + error = ofpacts_pull_openflow11_actions(msg, ob_len - sizeof *ob, + &ofpacts); + if (error) { + ofpbuf_uninit(&ofpacts); + ofputil_bucket_list_destroy(buckets); + return error; + } + + bucket = xzalloc(sizeof *bucket); + bucket->weight = ntohs(ob->weight); + error = ofputil_port_from_ofp11(ob->watch_port, &bucket->watch_port); + if (error) { + ofpbuf_uninit(&ofpacts); + ofputil_bucket_list_destroy(buckets); + return OFPERR_OFPGMFC_BAD_WATCH; + } + bucket->watch_group = ntohl(ob->watch_group); + bucket->ofpacts = ofpbuf_steal_data(&ofpacts); + bucket->ofpacts_len = ofpacts.size; + list_push_back(buckets, &bucket->list_node); + } + + return 0; +} + +/* Converts a group description reply in 'msg' into an abstract + * ofputil_group_desc in 'gd'. + * + * Multiple group description replies can be packed into a single OpenFlow + * message. Calling this function multiple times for a single 'msg' iterates + * through the replies. The caller must initially leave 'msg''s layer pointers + * null and not modify them between calls. + * + * Returns 0 if successful, EOF if no replies were left in this 'msg', + * otherwise a positive errno value. */ +int +ofputil_decode_group_desc_reply(struct ofputil_group_desc *gd, + struct ofpbuf *msg) +{ + struct ofp11_group_desc_stats *ogds; + size_t length; + + if (!msg->l2) { + ofpraw_pull_assert(msg); + } + + if (!msg->size) { + return EOF; + } + + ogds = ofpbuf_try_pull(msg, sizeof *ogds); + if (!ogds) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply has %zu " + "leftover bytes at end", msg->size); + return OFPERR_OFPBRC_BAD_LEN; + } + gd->type = ogds->type; + gd->group_id = ntohl(ogds->group_id); + + length = ntohs(ogds->length); + if (length < sizeof *ogds || length - sizeof *ogds > msg->size) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply claims invalid " + "length %zu", length); + return OFPERR_OFPBRC_BAD_LEN; + } + + return ofputil_pull_buckets(msg, length - sizeof *ogds, &gd->buckets); +} + +/* Converts abstract group mod 'gm' into a message for OpenFlow version + * 'ofp_version' and returns the message. */ +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 needs OpenFlow 1.1 or later " + "(\'-O OpenFlow11\')"); + } else if (gm->command == OFPGC11_MODIFY) { + ovs_fatal(0, "mod-group needs OpenFlow 1.1 or later " + "(\'-O OpenFlow11\')"); + } else { + ovs_fatal(0, "del-groups needs OpenFlow 1.1 or later " + "(\'-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; +} + +/* Converts OpenFlow group mod message 'oh' into an abstract group mod in + * 'gm'. Returns 0 if successful, otherwise an OpenFlow error code. */ +enum ofperr +ofputil_decode_group_mod(const struct ofp_header *oh, + struct ofputil_group_mod *gm) +{ + const struct ofp11_group_mod *ogm; + struct ofpbuf msg; + + ofpbuf_use_const(&msg, oh, ntohs(oh->length)); + ofpraw_pull_assert(&msg); + + ogm = ofpbuf_pull(&msg, sizeof *ogm); + gm->command = ntohs(ogm->command); + gm->type = ogm->type; + gm->group_id = ntohl(ogm->group_id); + + return ofputil_pull_buckets(&msg, msg.size, &gm->buckets); +} + /* Parse a queue status request message into 'oqsr'. * Returns 0 if successful, otherwise an OFPERR_* number. */ enum ofperr