mirror of
https://github.com/openvswitch/ovs
synced 2025-10-19 14:37:21 +00:00
ofputil_pull_bands() may change bands->data. Found by libfuzzer-ngram. Reported-by: Bhargava Shastry <bshastry@sect.tu-berlin.de> Signed-off-by: Ben Pfaff <blp@ovn.org> Reviewed-by: Yifeng Sun<pkusunyifeng@gmail.com>
631 lines
19 KiB
C
631 lines
19 KiB
C
/*
|
|
* Copyright (c) 2008-2017 Nicira, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "openvswitch/ofp-meter.h"
|
|
#include "byte-order.h"
|
|
#include "nx-match.h"
|
|
#include "openvswitch/ofp-errors.h"
|
|
#include "openvswitch/ofp-msgs.h"
|
|
#include "openvswitch/ofp-parse.h"
|
|
#include "openvswitch/ofpbuf.h"
|
|
#include "openvswitch/vlog.h"
|
|
|
|
VLOG_DEFINE_THIS_MODULE(ofp_meter);
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
|
|
|
static enum ofperr
|
|
ofputil_pull_bands(struct ofpbuf *msg, size_t len, uint16_t *n_bands,
|
|
struct ofpbuf *bands)
|
|
{
|
|
const struct ofp13_meter_band_header *ombh;
|
|
struct ofputil_meter_band *mb;
|
|
uint16_t n = 0;
|
|
|
|
ombh = ofpbuf_try_pull(msg, len);
|
|
if (!ombh) {
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
}
|
|
|
|
while (len >= sizeof (struct ofp13_meter_band_drop)) {
|
|
size_t ombh_len = ntohs(ombh->len);
|
|
/* All supported band types have the same length. */
|
|
if (ombh_len != sizeof (struct ofp13_meter_band_drop)) {
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
}
|
|
mb = ofpbuf_put_uninit(bands, sizeof *mb);
|
|
mb->type = ntohs(ombh->type);
|
|
if (mb->type != OFPMBT13_DROP && mb->type != OFPMBT13_DSCP_REMARK) {
|
|
return OFPERR_OFPMMFC_BAD_BAND;
|
|
}
|
|
mb->rate = ntohl(ombh->rate);
|
|
mb->burst_size = ntohl(ombh->burst_size);
|
|
mb->prec_level = (mb->type == OFPMBT13_DSCP_REMARK) ?
|
|
((struct ofp13_meter_band_dscp_remark *)ombh)->prec_level : 0;
|
|
n++;
|
|
len -= ombh_len;
|
|
ombh = ALIGNED_CAST(struct ofp13_meter_band_header *,
|
|
(char *) ombh + ombh_len);
|
|
}
|
|
if (len) {
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
}
|
|
*n_bands = n;
|
|
return 0;
|
|
}
|
|
|
|
enum ofperr
|
|
ofputil_decode_meter_mod(const struct ofp_header *oh,
|
|
struct ofputil_meter_mod *mm,
|
|
struct ofpbuf *bands)
|
|
{
|
|
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
|
|
ofpraw_pull_assert(&b);
|
|
const struct ofp13_meter_mod *omm = ofpbuf_pull(&b, sizeof *omm);
|
|
|
|
/* Translate the message. */
|
|
mm->command = ntohs(omm->command);
|
|
if (mm->command != OFPMC13_ADD &&
|
|
mm->command != OFPMC13_MODIFY &&
|
|
mm->command != OFPMC13_DELETE) {
|
|
return OFPERR_OFPMMFC_BAD_COMMAND;
|
|
}
|
|
mm->meter.meter_id = ntohl(omm->meter_id);
|
|
|
|
if (mm->command == OFPMC13_DELETE) {
|
|
mm->meter.flags = 0;
|
|
mm->meter.n_bands = 0;
|
|
mm->meter.bands = NULL;
|
|
} else {
|
|
enum ofperr error;
|
|
|
|
mm->meter.flags = ntohs(omm->flags);
|
|
if (mm->meter.flags & OFPMF13_KBPS &&
|
|
mm->meter.flags & OFPMF13_PKTPS) {
|
|
return OFPERR_OFPMMFC_BAD_FLAGS;
|
|
}
|
|
|
|
error = ofputil_pull_bands(&b, b.size, &mm->meter.n_bands, bands);
|
|
if (error) {
|
|
return error;
|
|
}
|
|
mm->meter.bands = bands->data;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ofputil_decode_meter_request(const struct ofp_header *oh, uint32_t *meter_id)
|
|
{
|
|
const struct ofp13_meter_multipart_request *omr = ofpmsg_body(oh);
|
|
*meter_id = ntohl(omr->meter_id);
|
|
}
|
|
|
|
struct ofpbuf *
|
|
ofputil_encode_meter_request(enum ofp_version ofp_version,
|
|
enum ofputil_meter_request_type type,
|
|
uint32_t meter_id)
|
|
{
|
|
struct ofpbuf *msg;
|
|
|
|
enum ofpraw raw;
|
|
|
|
switch (type) {
|
|
case OFPUTIL_METER_CONFIG:
|
|
raw = OFPRAW_OFPST13_METER_CONFIG_REQUEST;
|
|
break;
|
|
case OFPUTIL_METER_STATS:
|
|
raw = OFPRAW_OFPST13_METER_REQUEST;
|
|
break;
|
|
default:
|
|
case OFPUTIL_METER_FEATURES:
|
|
raw = OFPRAW_OFPST13_METER_FEATURES_REQUEST;
|
|
break;
|
|
}
|
|
|
|
msg = ofpraw_alloc(raw, ofp_version, 0);
|
|
|
|
if (type != OFPUTIL_METER_FEATURES) {
|
|
struct ofp13_meter_multipart_request *omr;
|
|
omr = ofpbuf_put_zeros(msg, sizeof *omr);
|
|
omr->meter_id = htonl(meter_id);
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
static void
|
|
ofputil_put_bands(uint16_t n_bands, const struct ofputil_meter_band *mb,
|
|
struct ofpbuf *msg)
|
|
{
|
|
uint16_t n = 0;
|
|
|
|
for (n = 0; n < n_bands; ++n) {
|
|
/* Currently all band types have same size. */
|
|
struct ofp13_meter_band_dscp_remark *ombh;
|
|
size_t ombh_len = sizeof *ombh;
|
|
|
|
ombh = ofpbuf_put_zeros(msg, ombh_len);
|
|
|
|
ombh->type = htons(mb->type);
|
|
ombh->len = htons(ombh_len);
|
|
ombh->rate = htonl(mb->rate);
|
|
ombh->burst_size = htonl(mb->burst_size);
|
|
ombh->prec_level = mb->prec_level;
|
|
|
|
mb++;
|
|
}
|
|
}
|
|
|
|
/* Encode a meter stat for 'mc' and append it to 'replies'. */
|
|
void
|
|
ofputil_append_meter_config(struct ovs_list *replies,
|
|
const struct ofputil_meter_config *mc)
|
|
{
|
|
struct ofpbuf *msg = ofpbuf_from_list(ovs_list_back(replies));
|
|
size_t start_ofs = msg->size;
|
|
struct ofp13_meter_config *reply;
|
|
|
|
ofpbuf_put_uninit(msg, sizeof *reply);
|
|
ofputil_put_bands(mc->n_bands, mc->bands, msg);
|
|
|
|
reply = ofpbuf_at_assert(msg, start_ofs, sizeof *reply);
|
|
reply->flags = htons(mc->flags);
|
|
reply->meter_id = htonl(mc->meter_id);
|
|
reply->length = htons(msg->size - start_ofs);
|
|
|
|
ofpmp_postappend(replies, start_ofs);
|
|
}
|
|
|
|
/* Encode a meter stat for 'ms' and append it to 'replies'. */
|
|
void
|
|
ofputil_append_meter_stats(struct ovs_list *replies,
|
|
const struct ofputil_meter_stats *ms)
|
|
{
|
|
struct ofp13_meter_stats *reply;
|
|
uint16_t n = 0;
|
|
uint16_t len;
|
|
|
|
len = sizeof *reply + ms->n_bands * sizeof(struct ofp13_meter_band_stats);
|
|
reply = ofpmp_append(replies, len);
|
|
|
|
reply->meter_id = htonl(ms->meter_id);
|
|
reply->len = htons(len);
|
|
memset(reply->pad, 0, sizeof reply->pad);
|
|
reply->flow_count = htonl(ms->flow_count);
|
|
reply->packet_in_count = htonll(ms->packet_in_count);
|
|
reply->byte_in_count = htonll(ms->byte_in_count);
|
|
reply->duration_sec = htonl(ms->duration_sec);
|
|
reply->duration_nsec = htonl(ms->duration_nsec);
|
|
|
|
for (n = 0; n < ms->n_bands; ++n) {
|
|
const struct ofputil_meter_band_stats *src = &ms->bands[n];
|
|
struct ofp13_meter_band_stats *dst = &reply->band_stats[n];
|
|
|
|
dst->packet_band_count = htonll(src->packet_count);
|
|
dst->byte_band_count = htonll(src->byte_count);
|
|
}
|
|
}
|
|
|
|
/* Converts an OFPMP_METER_CONFIG reply in 'msg' into an abstract
|
|
* ofputil_meter_config in 'mc', with mc->bands pointing to bands decoded into
|
|
* 'bands'. The caller must have initialized 'bands' and retains ownership of
|
|
* it across the call.
|
|
*
|
|
* Multiple OFPST13_METER_CONFIG replies can be packed into a single OpenFlow
|
|
* message. Calling this function multiple times for a single 'msg' iterates
|
|
* through the replies. 'bands' is cleared for each reply.
|
|
*
|
|
* Returns 0 if successful, EOF if no replies were left in this 'msg',
|
|
* otherwise a positive errno value. */
|
|
int
|
|
ofputil_decode_meter_config(struct ofpbuf *msg,
|
|
struct ofputil_meter_config *mc,
|
|
struct ofpbuf *bands)
|
|
{
|
|
const struct ofp13_meter_config *omc;
|
|
enum ofperr err;
|
|
|
|
/* Pull OpenFlow headers for the first call. */
|
|
if (!msg->header) {
|
|
ofpraw_pull_assert(msg);
|
|
}
|
|
|
|
if (!msg->size) {
|
|
return EOF;
|
|
}
|
|
|
|
omc = ofpbuf_try_pull(msg, sizeof *omc);
|
|
if (!omc) {
|
|
VLOG_WARN_RL(&rl, "OFPMP_METER_CONFIG reply has %"PRIu32" leftover "
|
|
"bytes at end", msg->size);
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
}
|
|
|
|
ofpbuf_clear(bands);
|
|
err = ofputil_pull_bands(msg, ntohs(omc->length) - sizeof *omc,
|
|
&mc->n_bands, bands);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
mc->meter_id = ntohl(omc->meter_id);
|
|
mc->flags = ntohs(omc->flags);
|
|
mc->bands = bands->data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static enum ofperr
|
|
ofputil_pull_band_stats(struct ofpbuf *msg, size_t len, uint16_t *n_bands,
|
|
struct ofpbuf *bands)
|
|
{
|
|
const struct ofp13_meter_band_stats *ombs;
|
|
struct ofputil_meter_band_stats *mbs;
|
|
uint16_t n, i;
|
|
|
|
ombs = ofpbuf_try_pull(msg, len);
|
|
if (!ombs) {
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
}
|
|
|
|
n = len / sizeof *ombs;
|
|
if (len != n * sizeof *ombs) {
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
}
|
|
|
|
mbs = ofpbuf_put_uninit(bands, len);
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
mbs[i].packet_count = ntohll(ombs[i].packet_band_count);
|
|
mbs[i].byte_count = ntohll(ombs[i].byte_band_count);
|
|
}
|
|
*n_bands = n;
|
|
return 0;
|
|
}
|
|
|
|
/* Converts an OFPMP_METER reply in 'msg' into an abstract
|
|
* ofputil_meter_stats in 'ms', with ms->bands pointing to band stats
|
|
* decoded into 'bands'.
|
|
*
|
|
* Multiple OFPMP_METER replies can be packed into a single OpenFlow
|
|
* message. Calling this function multiple times for a single 'msg' iterates
|
|
* through the replies. 'bands' is cleared for each reply.
|
|
*
|
|
* Returns 0 if successful, EOF if no replies were left in this 'msg',
|
|
* otherwise a positive errno value. */
|
|
int
|
|
ofputil_decode_meter_stats(struct ofpbuf *msg,
|
|
struct ofputil_meter_stats *ms,
|
|
struct ofpbuf *bands)
|
|
{
|
|
const struct ofp13_meter_stats *oms;
|
|
enum ofperr err;
|
|
|
|
/* Pull OpenFlow headers for the first call. */
|
|
if (!msg->header) {
|
|
ofpraw_pull_assert(msg);
|
|
}
|
|
|
|
if (!msg->size) {
|
|
return EOF;
|
|
}
|
|
|
|
oms = ofpbuf_try_pull(msg, sizeof *oms);
|
|
if (!oms) {
|
|
VLOG_WARN_RL(&rl, "OFPMP_METER reply has %"PRIu32" leftover bytes "
|
|
"at end", msg->size);
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
}
|
|
|
|
ofpbuf_clear(bands);
|
|
err = ofputil_pull_band_stats(msg, ntohs(oms->len) - sizeof *oms,
|
|
&ms->n_bands, bands);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
ms->meter_id = ntohl(oms->meter_id);
|
|
ms->flow_count = ntohl(oms->flow_count);
|
|
ms->packet_in_count = ntohll(oms->packet_in_count);
|
|
ms->byte_in_count = ntohll(oms->byte_in_count);
|
|
ms->duration_sec = ntohl(oms->duration_sec);
|
|
ms->duration_nsec = ntohl(oms->duration_nsec);
|
|
ms->bands = bands->data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ofputil_decode_meter_features(const struct ofp_header *oh,
|
|
struct ofputil_meter_features *mf)
|
|
{
|
|
const struct ofp13_meter_features *omf = ofpmsg_body(oh);
|
|
|
|
mf->max_meters = ntohl(omf->max_meter);
|
|
mf->band_types = ntohl(omf->band_types);
|
|
mf->capabilities = ntohl(omf->capabilities);
|
|
mf->max_bands = omf->max_bands;
|
|
mf->max_color = omf->max_color;
|
|
}
|
|
|
|
struct ofpbuf *
|
|
ofputil_encode_meter_features_reply(const struct ofputil_meter_features *mf,
|
|
const struct ofp_header *request)
|
|
{
|
|
struct ofpbuf *reply;
|
|
struct ofp13_meter_features *omf;
|
|
|
|
reply = ofpraw_alloc_stats_reply(request, 0);
|
|
omf = ofpbuf_put_zeros(reply, sizeof *omf);
|
|
|
|
omf->max_meter = htonl(mf->max_meters);
|
|
omf->band_types = htonl(mf->band_types);
|
|
omf->capabilities = htonl(mf->capabilities);
|
|
omf->max_bands = mf->max_bands;
|
|
omf->max_color = mf->max_color;
|
|
|
|
return reply;
|
|
}
|
|
|
|
struct ofpbuf *
|
|
ofputil_encode_meter_mod(enum ofp_version ofp_version,
|
|
const struct ofputil_meter_mod *mm)
|
|
{
|
|
struct ofpbuf *msg;
|
|
|
|
struct ofp13_meter_mod *omm;
|
|
|
|
msg = ofpraw_alloc(OFPRAW_OFPT13_METER_MOD, ofp_version,
|
|
NXM_TYPICAL_LEN + mm->meter.n_bands * 16);
|
|
omm = ofpbuf_put_zeros(msg, sizeof *omm);
|
|
omm->command = htons(mm->command);
|
|
if (mm->command != OFPMC13_DELETE) {
|
|
omm->flags = htons(mm->meter.flags);
|
|
}
|
|
omm->meter_id = htonl(mm->meter.meter_id);
|
|
|
|
ofputil_put_bands(mm->meter.n_bands, mm->meter.bands, msg);
|
|
|
|
ofpmsg_update_length(msg);
|
|
return msg;
|
|
}
|
|
|
|
/* Parse a string representation of a meter modification message to '*mm'.
|
|
* If successful, 'mm->meter.bands' must be free()d by the caller. */
|
|
static char * OVS_WARN_UNUSED_RESULT
|
|
parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string,
|
|
struct ofpbuf *bands, int command,
|
|
enum ofputil_protocol *usable_protocols)
|
|
{
|
|
enum {
|
|
F_METER = 1 << 0,
|
|
F_FLAGS = 1 << 1,
|
|
F_BANDS = 1 << 2,
|
|
} fields;
|
|
char *save_ptr = NULL;
|
|
char *band_str = NULL;
|
|
char *name;
|
|
|
|
/* Meters require at least OF 1.3. */
|
|
*usable_protocols = OFPUTIL_P_OF13_UP;
|
|
|
|
switch (command) {
|
|
case -1:
|
|
fields = F_METER;
|
|
break;
|
|
|
|
case OFPMC13_ADD:
|
|
fields = F_METER | F_FLAGS | F_BANDS;
|
|
break;
|
|
|
|
case OFPMC13_DELETE:
|
|
fields = F_METER;
|
|
break;
|
|
|
|
case OFPMC13_MODIFY:
|
|
fields = F_METER | F_FLAGS | F_BANDS;
|
|
break;
|
|
|
|
default:
|
|
OVS_NOT_REACHED();
|
|
}
|
|
|
|
mm->command = command;
|
|
mm->meter.meter_id = 0;
|
|
mm->meter.flags = 0;
|
|
mm->meter.n_bands = 0;
|
|
mm->meter.bands = NULL;
|
|
|
|
if (fields & F_BANDS) {
|
|
band_str = strstr(string, "band");
|
|
if (!band_str) {
|
|
return xstrdup("must specify bands");
|
|
}
|
|
*band_str = '\0';
|
|
|
|
band_str = strchr(band_str + 1, '=');
|
|
if (!band_str) {
|
|
return xstrdup("must specify bands");
|
|
}
|
|
|
|
band_str++;
|
|
}
|
|
for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
|
|
name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
|
|
|
|
if (fields & F_FLAGS && !strcmp(name, "kbps")) {
|
|
mm->meter.flags |= OFPMF13_KBPS;
|
|
} else if (fields & F_FLAGS && !strcmp(name, "pktps")) {
|
|
mm->meter.flags |= OFPMF13_PKTPS;
|
|
} else if (fields & F_FLAGS && !strcmp(name, "burst")) {
|
|
mm->meter.flags |= OFPMF13_BURST;
|
|
} else if (fields & F_FLAGS && !strcmp(name, "stats")) {
|
|
mm->meter.flags |= OFPMF13_STATS;
|
|
} else {
|
|
char *value;
|
|
|
|
value = strtok_r(NULL, ", \t\r\n", &save_ptr);
|
|
if (!value) {
|
|
return xasprintf("field %s missing value", name);
|
|
}
|
|
|
|
if (!strcmp(name, "meter")) {
|
|
if (!strcmp(value, "all")) {
|
|
mm->meter.meter_id = OFPM13_ALL;
|
|
} else if (!strcmp(value, "controller")) {
|
|
mm->meter.meter_id = OFPM13_CONTROLLER;
|
|
} else if (!strcmp(value, "slowpath")) {
|
|
mm->meter.meter_id = OFPM13_SLOWPATH;
|
|
} else {
|
|
char *error = str_to_u32(value, &mm->meter.meter_id);
|
|
if (error) {
|
|
return error;
|
|
}
|
|
if (mm->meter.meter_id > OFPM13_MAX
|
|
|| !mm->meter.meter_id) {
|
|
return xasprintf("invalid value for %s", name);
|
|
}
|
|
}
|
|
} else {
|
|
return xasprintf("unknown keyword %s", name);
|
|
}
|
|
}
|
|
}
|
|
if (fields & F_METER && !mm->meter.meter_id) {
|
|
return xstrdup("must specify 'meter'");
|
|
}
|
|
if (fields & F_FLAGS && !mm->meter.flags) {
|
|
return xstrdup("meter must specify either 'kbps' or 'pktps'");
|
|
}
|
|
|
|
if (fields & F_BANDS) {
|
|
uint16_t n_bands = 0;
|
|
struct ofputil_meter_band *band = NULL;
|
|
int i;
|
|
|
|
for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name;
|
|
name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
|
|
|
|
char *value;
|
|
|
|
value = strtok_r(NULL, ", \t\r\n", &save_ptr);
|
|
if (!value) {
|
|
return xasprintf("field %s missing value", name);
|
|
}
|
|
|
|
if (!strcmp(name, "type")) {
|
|
/* Start a new band */
|
|
band = ofpbuf_put_zeros(bands, sizeof *band);
|
|
n_bands++;
|
|
|
|
if (!strcmp(value, "drop")) {
|
|
band->type = OFPMBT13_DROP;
|
|
} else if (!strcmp(value, "dscp_remark")) {
|
|
band->type = OFPMBT13_DSCP_REMARK;
|
|
} else {
|
|
return xasprintf("field %s unknown value %s", name, value);
|
|
}
|
|
} else if (!band || !band->type) {
|
|
return xstrdup("band must start with the 'type' keyword");
|
|
} else if (!strcmp(name, "rate")) {
|
|
char *error = str_to_u32(value, &band->rate);
|
|
if (error) {
|
|
return error;
|
|
}
|
|
} else if (!strcmp(name, "burst_size")) {
|
|
char *error = str_to_u32(value, &band->burst_size);
|
|
if (error) {
|
|
return error;
|
|
}
|
|
} else if (!strcmp(name, "prec_level")) {
|
|
char *error = str_to_u8(value, name, &band->prec_level);
|
|
if (error) {
|
|
return error;
|
|
}
|
|
} else {
|
|
return xasprintf("unknown keyword %s", name);
|
|
}
|
|
}
|
|
/* validate bands */
|
|
if (!n_bands) {
|
|
return xstrdup("meter must have bands");
|
|
}
|
|
|
|
mm->meter.n_bands = n_bands;
|
|
mm->meter.bands = ofpbuf_steal_data(bands);
|
|
|
|
for (i = 0; i < n_bands; ++i) {
|
|
band = &mm->meter.bands[i];
|
|
|
|
if (!band->type) {
|
|
return xstrdup("band must have 'type'");
|
|
}
|
|
if (band->type == OFPMBT13_DSCP_REMARK) {
|
|
if (!band->prec_level) {
|
|
return xstrdup("'dscp_remark' band must have"
|
|
" 'prec_level'");
|
|
}
|
|
} else {
|
|
if (band->prec_level) {
|
|
return xstrdup("Only 'dscp_remark' band may have"
|
|
" 'prec_level'");
|
|
}
|
|
}
|
|
if (!band->rate) {
|
|
return xstrdup("band must have 'rate'");
|
|
}
|
|
if (mm->meter.flags & OFPMF13_BURST) {
|
|
if (!band->burst_size) {
|
|
return xstrdup("band must have 'burst_size' "
|
|
"when 'burst' flag is set");
|
|
}
|
|
} else {
|
|
if (band->burst_size) {
|
|
return xstrdup("band may have 'burst_size' only "
|
|
"when 'burst' flag is set");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
|
|
* page) into 'mm' for sending the specified meter_mod 'command' to a switch.
|
|
*
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
* error. The caller is responsible for freeing the returned string.
|
|
* If successful, 'mm->meter.bands' must be free()d by the caller. */
|
|
char * OVS_WARN_UNUSED_RESULT
|
|
parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
|
|
int command, enum ofputil_protocol *usable_protocols)
|
|
{
|
|
struct ofpbuf bands;
|
|
char *string;
|
|
char *error;
|
|
|
|
ofpbuf_init(&bands, 64);
|
|
string = xstrdup(str_);
|
|
|
|
error = parse_ofp_meter_mod_str__(mm, string, &bands, command,
|
|
usable_protocols);
|
|
|
|
free(string);
|
|
ofpbuf_uninit(&bands);
|
|
|
|
return error;
|
|
}
|