2 * Copyright (c) 2008-2017 Nicira, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include "openvswitch/ofp-meter.h"
19 #include "byte-order.h"
21 #include "openvswitch/ofp-errors.h"
22 #include "openvswitch/ofp-msgs.h"
23 #include "openvswitch/ofp-parse.h"
24 #include "openvswitch/ofp-print.h"
25 #include "openvswitch/ofpbuf.h"
26 #include "openvswitch/vlog.h"
28 VLOG_DEFINE_THIS_MODULE(ofp_meter
);
30 static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(1, 5);
33 ofputil_format_meter_id(struct ds
*s
, uint32_t meter_id
, char separator
)
35 if (meter_id
<= OFPM13_MAX
) {
36 ds_put_format(s
, "meter%c%"PRIu32
, separator
, meter_id
);
43 case OFPM13_CONTROLLER
:
52 ds_put_format(s
, "meter%c%s", separator
, name
);
57 ofputil_format_meter_band(struct ds
*s
, enum ofp13_meter_flags flags
,
58 const struct ofputil_meter_band
*mb
)
60 ds_put_cstr(s
, "\ntype=");
63 ds_put_cstr(s
, "drop");
65 case OFPMBT13_DSCP_REMARK
:
66 ds_put_cstr(s
, "dscp_remark");
69 ds_put_format(s
, "%u", mb
->type
);
72 ds_put_format(s
, " rate=%"PRIu32
, mb
->rate
);
74 if (flags
& OFPMF13_BURST
) {
75 ds_put_format(s
, " burst_size=%"PRIu32
, mb
->burst_size
);
77 if (mb
->type
== OFPMBT13_DSCP_REMARK
) {
78 ds_put_format(s
, " prec_level=%"PRIu8
, mb
->prec_level
);
83 ofputil_pull_bands(struct ofpbuf
*msg
, size_t len
, uint16_t *n_bands
,
86 const struct ofp13_meter_band_header
*ombh
;
87 struct ofputil_meter_band
*mb
;
90 ombh
= ofpbuf_try_pull(msg
, len
);
92 return OFPERR_OFPBRC_BAD_LEN
;
95 while (len
>= sizeof (struct ofp13_meter_band_drop
)) {
96 size_t ombh_len
= ntohs(ombh
->len
);
97 /* All supported band types have the same length. */
98 if (ombh_len
!= sizeof (struct ofp13_meter_band_drop
)) {
99 return OFPERR_OFPBRC_BAD_LEN
;
101 mb
= ofpbuf_put_uninit(bands
, sizeof *mb
);
102 mb
->type
= ntohs(ombh
->type
);
103 if (mb
->type
!= OFPMBT13_DROP
&& mb
->type
!= OFPMBT13_DSCP_REMARK
) {
104 return OFPERR_OFPMMFC_BAD_BAND
;
106 mb
->rate
= ntohl(ombh
->rate
);
107 mb
->burst_size
= ntohl(ombh
->burst_size
);
108 mb
->prec_level
= (mb
->type
== OFPMBT13_DSCP_REMARK
) ?
109 ((struct ofp13_meter_band_dscp_remark
*)ombh
)->prec_level
: 0;
112 ombh
= ALIGNED_CAST(struct ofp13_meter_band_header
*,
113 (char *) ombh
+ ombh_len
);
116 return OFPERR_OFPBRC_BAD_LEN
;
123 ofputil_decode_meter_mod(const struct ofp_header
*oh
,
124 struct ofputil_meter_mod
*mm
,
125 struct ofpbuf
*bands
)
127 struct ofpbuf b
= ofpbuf_const_initializer(oh
, ntohs(oh
->length
));
128 ofpraw_pull_assert(&b
);
129 const struct ofp13_meter_mod
*omm
= ofpbuf_pull(&b
, sizeof *omm
);
131 /* Translate the message. */
132 mm
->command
= ntohs(omm
->command
);
133 if (mm
->command
!= OFPMC13_ADD
&&
134 mm
->command
!= OFPMC13_MODIFY
&&
135 mm
->command
!= OFPMC13_DELETE
) {
136 return OFPERR_OFPMMFC_BAD_COMMAND
;
138 mm
->meter
.meter_id
= ntohl(omm
->meter_id
);
140 if (mm
->command
== OFPMC13_DELETE
) {
142 mm
->meter
.n_bands
= 0;
143 mm
->meter
.bands
= NULL
;
147 mm
->meter
.flags
= ntohs(omm
->flags
);
148 if (mm
->meter
.flags
& OFPMF13_KBPS
&&
149 mm
->meter
.flags
& OFPMF13_PKTPS
) {
150 return OFPERR_OFPMMFC_BAD_FLAGS
;
153 error
= ofputil_pull_bands(&b
, b
.size
, &mm
->meter
.n_bands
, bands
);
157 mm
->meter
.bands
= bands
->data
;
163 ofputil_decode_meter_request(const struct ofp_header
*oh
, uint32_t *meter_id
)
165 const struct ofp13_meter_multipart_request
*omr
= ofpmsg_body(oh
);
166 *meter_id
= ntohl(omr
->meter_id
);
170 ofputil_encode_meter_request(enum ofp_version ofp_version
,
171 enum ofputil_meter_request_type type
,
179 case OFPUTIL_METER_CONFIG
:
180 raw
= OFPRAW_OFPST13_METER_CONFIG_REQUEST
;
182 case OFPUTIL_METER_STATS
:
183 raw
= OFPRAW_OFPST13_METER_REQUEST
;
186 case OFPUTIL_METER_FEATURES
:
187 raw
= OFPRAW_OFPST13_METER_FEATURES_REQUEST
;
191 msg
= ofpraw_alloc(raw
, ofp_version
, 0);
193 if (type
!= OFPUTIL_METER_FEATURES
) {
194 struct ofp13_meter_multipart_request
*omr
;
195 omr
= ofpbuf_put_zeros(msg
, sizeof *omr
);
196 omr
->meter_id
= htonl(meter_id
);
202 ofputil_put_bands(uint16_t n_bands
, const struct ofputil_meter_band
*mb
,
207 for (n
= 0; n
< n_bands
; ++n
) {
208 /* Currently all band types have same size. */
209 struct ofp13_meter_band_dscp_remark
*ombh
;
210 size_t ombh_len
= sizeof *ombh
;
212 ombh
= ofpbuf_put_zeros(msg
, ombh_len
);
214 ombh
->type
= htons(mb
->type
);
215 ombh
->len
= htons(ombh_len
);
216 ombh
->rate
= htonl(mb
->rate
);
217 ombh
->burst_size
= htonl(mb
->burst_size
);
218 ombh
->prec_level
= mb
->prec_level
;
224 /* Encode a meter stat for 'mc' and append it to 'replies'. */
226 ofputil_append_meter_config(struct ovs_list
*replies
,
227 const struct ofputil_meter_config
*mc
)
229 struct ofpbuf
*msg
= ofpbuf_from_list(ovs_list_back(replies
));
230 size_t start_ofs
= msg
->size
;
231 struct ofp13_meter_config
*reply
;
233 ofpbuf_put_uninit(msg
, sizeof *reply
);
234 ofputil_put_bands(mc
->n_bands
, mc
->bands
, msg
);
236 reply
= ofpbuf_at_assert(msg
, start_ofs
, sizeof *reply
);
237 reply
->flags
= htons(mc
->flags
);
238 reply
->meter_id
= htonl(mc
->meter_id
);
239 reply
->length
= htons(msg
->size
- start_ofs
);
241 ofpmp_postappend(replies
, start_ofs
);
244 /* Encode a meter stat for 'ms' and append it to 'replies'. */
246 ofputil_append_meter_stats(struct ovs_list
*replies
,
247 const struct ofputil_meter_stats
*ms
)
249 struct ofp13_meter_stats
*reply
;
253 len
= sizeof *reply
+ ms
->n_bands
* sizeof(struct ofp13_meter_band_stats
);
254 reply
= ofpmp_append(replies
, len
);
256 reply
->meter_id
= htonl(ms
->meter_id
);
257 reply
->len
= htons(len
);
258 memset(reply
->pad
, 0, sizeof reply
->pad
);
259 reply
->flow_count
= htonl(ms
->flow_count
);
260 reply
->packet_in_count
= htonll(ms
->packet_in_count
);
261 reply
->byte_in_count
= htonll(ms
->byte_in_count
);
262 reply
->duration_sec
= htonl(ms
->duration_sec
);
263 reply
->duration_nsec
= htonl(ms
->duration_nsec
);
265 for (n
= 0; n
< ms
->n_bands
; ++n
) {
266 const struct ofputil_meter_band_stats
*src
= &ms
->bands
[n
];
267 struct ofp13_meter_band_stats
*dst
= &reply
->band_stats
[n
];
269 dst
->packet_band_count
= htonll(src
->packet_count
);
270 dst
->byte_band_count
= htonll(src
->byte_count
);
274 /* Converts an OFPMP_METER_CONFIG reply in 'msg' into an abstract
275 * ofputil_meter_config in 'mc', with mc->bands pointing to bands decoded into
276 * 'bands'. The caller must have initialized 'bands' and retains ownership of
277 * it across the call.
279 * Multiple OFPST13_METER_CONFIG replies can be packed into a single OpenFlow
280 * message. Calling this function multiple times for a single 'msg' iterates
281 * through the replies. 'bands' is cleared for each reply.
283 * Returns 0 if successful, EOF if no replies were left in this 'msg',
284 * otherwise a positive errno value. */
286 ofputil_decode_meter_config(struct ofpbuf
*msg
,
287 struct ofputil_meter_config
*mc
,
288 struct ofpbuf
*bands
)
290 const struct ofp13_meter_config
*omc
;
293 /* Pull OpenFlow headers for the first call. */
295 ofpraw_pull_assert(msg
);
302 omc
= ofpbuf_try_pull(msg
, sizeof *omc
);
304 VLOG_WARN_RL(&rl
, "OFPMP_METER_CONFIG reply has %"PRIu32
" leftover "
305 "bytes at end", msg
->size
);
306 return OFPERR_OFPBRC_BAD_LEN
;
310 err
= ofputil_pull_bands(msg
, ntohs(omc
->length
) - sizeof *omc
,
311 &mc
->n_bands
, bands
);
315 mc
->meter_id
= ntohl(omc
->meter_id
);
316 mc
->flags
= ntohs(omc
->flags
);
317 mc
->bands
= bands
->data
;
323 ofp_print_meter_flags(struct ds
*s
, enum ofp13_meter_flags flags
)
325 if (flags
& OFPMF13_KBPS
) {
326 ds_put_cstr(s
, "kbps ");
328 if (flags
& OFPMF13_PKTPS
) {
329 ds_put_cstr(s
, "pktps ");
331 if (flags
& OFPMF13_BURST
) {
332 ds_put_cstr(s
, "burst ");
334 if (flags
& OFPMF13_STATS
) {
335 ds_put_cstr(s
, "stats ");
338 flags
&= ~(OFPMF13_KBPS
| OFPMF13_PKTPS
| OFPMF13_BURST
| OFPMF13_STATS
);
340 ds_put_format(s
, "flags:0x%x ", (unsigned)flags
);
345 ofputil_format_meter_config(struct ds
*s
,
346 const struct ofputil_meter_config
*mc
)
350 ofputil_format_meter_id(s
, mc
->meter_id
, '=');
353 ofp_print_meter_flags(s
, mc
->flags
);
355 ds_put_cstr(s
, "bands=");
356 for (i
= 0; i
< mc
->n_bands
; ++i
) {
357 ofputil_format_meter_band(s
, mc
->flags
, &mc
->bands
[i
]);
359 ds_put_char(s
, '\n');
363 ofputil_pull_band_stats(struct ofpbuf
*msg
, size_t len
, uint16_t *n_bands
,
364 struct ofpbuf
*bands
)
366 const struct ofp13_meter_band_stats
*ombs
;
367 struct ofputil_meter_band_stats
*mbs
;
370 ombs
= ofpbuf_try_pull(msg
, len
);
372 return OFPERR_OFPBRC_BAD_LEN
;
375 n
= len
/ sizeof *ombs
;
376 if (len
!= n
* sizeof *ombs
) {
377 return OFPERR_OFPBRC_BAD_LEN
;
380 mbs
= ofpbuf_put_uninit(bands
, len
);
382 for (i
= 0; i
< n
; ++i
) {
383 mbs
[i
].packet_count
= ntohll(ombs
[i
].packet_band_count
);
384 mbs
[i
].byte_count
= ntohll(ombs
[i
].byte_band_count
);
390 /* Converts an OFPMP_METER reply in 'msg' into an abstract
391 * ofputil_meter_stats in 'ms', with ms->bands pointing to band stats
392 * decoded into 'bands'.
394 * Multiple OFPMP_METER replies can be packed into a single OpenFlow
395 * message. Calling this function multiple times for a single 'msg' iterates
396 * through the replies. 'bands' is cleared for each reply.
398 * Returns 0 if successful, EOF if no replies were left in this 'msg',
399 * otherwise a positive errno value. */
401 ofputil_decode_meter_stats(struct ofpbuf
*msg
,
402 struct ofputil_meter_stats
*ms
,
403 struct ofpbuf
*bands
)
405 const struct ofp13_meter_stats
*oms
;
408 /* Pull OpenFlow headers for the first call. */
410 ofpraw_pull_assert(msg
);
417 oms
= ofpbuf_try_pull(msg
, sizeof *oms
);
419 VLOG_WARN_RL(&rl
, "OFPMP_METER reply has %"PRIu32
" leftover bytes "
420 "at end", msg
->size
);
421 return OFPERR_OFPBRC_BAD_LEN
;
425 err
= ofputil_pull_band_stats(msg
, ntohs(oms
->len
) - sizeof *oms
,
426 &ms
->n_bands
, bands
);
430 ms
->meter_id
= ntohl(oms
->meter_id
);
431 ms
->flow_count
= ntohl(oms
->flow_count
);
432 ms
->packet_in_count
= ntohll(oms
->packet_in_count
);
433 ms
->byte_in_count
= ntohll(oms
->byte_in_count
);
434 ms
->duration_sec
= ntohl(oms
->duration_sec
);
435 ms
->duration_nsec
= ntohl(oms
->duration_nsec
);
436 ms
->bands
= bands
->data
;
442 ofputil_format_meter_stats(struct ds
*s
, const struct ofputil_meter_stats
*ms
)
446 ofputil_format_meter_id(s
, ms
->meter_id
, ':');
448 ds_put_format(s
, "flow_count:%"PRIu32
" ", ms
->flow_count
);
449 ds_put_format(s
, "packet_in_count:%"PRIu64
" ", ms
->packet_in_count
);
450 ds_put_format(s
, "byte_in_count:%"PRIu64
" ", ms
->byte_in_count
);
451 ds_put_cstr(s
, "duration:");
452 ofp_print_duration(s
, ms
->duration_sec
, ms
->duration_nsec
);
455 ds_put_cstr(s
, "bands:\n");
456 for (i
= 0; i
< ms
->n_bands
; ++i
) {
457 ds_put_format(s
, "%d: ", i
);
458 ds_put_format(s
, "packet_count:%"PRIu64
" ", ms
->bands
[i
].packet_count
);
459 ds_put_format(s
, "byte_count:%"PRIu64
"\n", ms
->bands
[i
].byte_count
);
464 ofputil_decode_meter_features(const struct ofp_header
*oh
,
465 struct ofputil_meter_features
*mf
)
467 const struct ofp13_meter_features
*omf
= ofpmsg_body(oh
);
469 mf
->max_meters
= ntohl(omf
->max_meter
);
470 mf
->band_types
= ntohl(omf
->band_types
);
471 mf
->capabilities
= ntohl(omf
->capabilities
);
472 mf
->max_bands
= omf
->max_bands
;
473 mf
->max_color
= omf
->max_color
;
477 ofputil_encode_meter_features_reply(const struct ofputil_meter_features
*mf
,
478 const struct ofp_header
*request
)
480 struct ofpbuf
*reply
;
481 struct ofp13_meter_features
*omf
;
483 reply
= ofpraw_alloc_stats_reply(request
, 0);
484 omf
= ofpbuf_put_zeros(reply
, sizeof *omf
);
486 omf
->max_meter
= htonl(mf
->max_meters
);
487 omf
->band_types
= htonl(mf
->band_types
);
488 omf
->capabilities
= htonl(mf
->capabilities
);
489 omf
->max_bands
= mf
->max_bands
;
490 omf
->max_color
= mf
->max_color
;
496 ofputil_meter_band_types_to_name(uint32_t bit
)
499 case 1 << OFPMBT13_DROP
: return "drop";
500 case 1 << OFPMBT13_DSCP_REMARK
: return "dscp_remark";
507 ofputil_meter_capabilities_to_name(uint32_t bit
)
509 enum ofp13_meter_flags flag
= bit
;
512 case OFPMF13_KBPS
: return "kbps";
513 case OFPMF13_PKTPS
: return "pktps";
514 case OFPMF13_BURST
: return "burst";
515 case OFPMF13_STATS
: return "stats";
522 ofputil_format_meter_features(struct ds
*s
,
523 const struct ofputil_meter_features
*mf
)
525 ds_put_format(s
, "\nmax_meter:%"PRIu32
, mf
->max_meters
);
526 ds_put_format(s
, " max_bands:%"PRIu8
, mf
->max_bands
);
527 ds_put_format(s
, " max_color:%"PRIu8
"\n", mf
->max_color
);
529 ds_put_cstr(s
, "band_types: ");
530 ofp_print_bit_names(s
, mf
->band_types
,
531 ofputil_meter_band_types_to_name
, ' ');
532 ds_put_char(s
, '\n');
534 ds_put_cstr(s
, "capabilities: ");
535 ofp_print_bit_names(s
, mf
->capabilities
,
536 ofputil_meter_capabilities_to_name
, ' ');
537 ds_put_char(s
, '\n');
541 ofputil_encode_meter_mod(enum ofp_version ofp_version
,
542 const struct ofputil_meter_mod
*mm
)
546 struct ofp13_meter_mod
*omm
;
548 msg
= ofpraw_alloc(OFPRAW_OFPT13_METER_MOD
, ofp_version
,
549 NXM_TYPICAL_LEN
+ mm
->meter
.n_bands
* 16);
550 omm
= ofpbuf_put_zeros(msg
, sizeof *omm
);
551 omm
->command
= htons(mm
->command
);
552 if (mm
->command
!= OFPMC13_DELETE
) {
553 omm
->flags
= htons(mm
->meter
.flags
);
555 omm
->meter_id
= htonl(mm
->meter
.meter_id
);
557 ofputil_put_bands(mm
->meter
.n_bands
, mm
->meter
.bands
, msg
);
559 ofpmsg_update_length(msg
);
563 /* Parse a string representation of a meter modification message to '*mm'.
564 * If successful, 'mm->meter.bands' must be free()d by the caller. */
565 static char * OVS_WARN_UNUSED_RESULT
566 parse_ofp_meter_mod_str__(struct ofputil_meter_mod
*mm
, char *string
,
567 struct ofpbuf
*bands
, int command
,
568 enum ofputil_protocol
*usable_protocols
)
575 char *save_ptr
= NULL
;
576 char *band_str
= NULL
;
579 /* Meters require at least OF 1.3. */
580 *usable_protocols
= OFPUTIL_P_OF13_UP
;
588 fields
= F_METER
| F_FLAGS
| F_BANDS
;
596 fields
= F_METER
| F_FLAGS
| F_BANDS
;
603 mm
->command
= command
;
604 mm
->meter
.meter_id
= 0;
606 mm
->meter
.n_bands
= 0;
607 mm
->meter
.bands
= NULL
;
609 if (fields
& F_BANDS
) {
610 band_str
= strstr(string
, "band");
612 return xstrdup("must specify bands");
616 band_str
= strchr(band_str
+ 1, '=');
618 return xstrdup("must specify bands");
623 for (name
= strtok_r(string
, "=, \t\r\n", &save_ptr
); name
;
624 name
= strtok_r(NULL
, "=, \t\r\n", &save_ptr
)) {
626 if (fields
& F_FLAGS
&& !strcmp(name
, "kbps")) {
627 mm
->meter
.flags
|= OFPMF13_KBPS
;
628 } else if (fields
& F_FLAGS
&& !strcmp(name
, "pktps")) {
629 mm
->meter
.flags
|= OFPMF13_PKTPS
;
630 } else if (fields
& F_FLAGS
&& !strcmp(name
, "burst")) {
631 mm
->meter
.flags
|= OFPMF13_BURST
;
632 } else if (fields
& F_FLAGS
&& !strcmp(name
, "stats")) {
633 mm
->meter
.flags
|= OFPMF13_STATS
;
637 value
= strtok_r(NULL
, ", \t\r\n", &save_ptr
);
639 return xasprintf("field %s missing value", name
);
642 if (!strcmp(name
, "meter")) {
643 if (!strcmp(value
, "all")) {
644 mm
->meter
.meter_id
= OFPM13_ALL
;
645 } else if (!strcmp(value
, "controller")) {
646 mm
->meter
.meter_id
= OFPM13_CONTROLLER
;
647 } else if (!strcmp(value
, "slowpath")) {
648 mm
->meter
.meter_id
= OFPM13_SLOWPATH
;
650 char *error
= str_to_u32(value
, &mm
->meter
.meter_id
);
654 if (mm
->meter
.meter_id
> OFPM13_MAX
655 || !mm
->meter
.meter_id
) {
656 return xasprintf("invalid value for %s", name
);
660 return xasprintf("unknown keyword %s", name
);
664 if (fields
& F_METER
&& !mm
->meter
.meter_id
) {
665 return xstrdup("must specify 'meter'");
667 if (fields
& F_FLAGS
&& !mm
->meter
.flags
) {
668 return xstrdup("meter must specify either 'kbps' or 'pktps'");
671 if (fields
& F_BANDS
) {
672 uint16_t n_bands
= 0;
673 struct ofputil_meter_band
*band
= NULL
;
676 for (name
= strtok_r(band_str
, "=, \t\r\n", &save_ptr
); name
;
677 name
= strtok_r(NULL
, "=, \t\r\n", &save_ptr
)) {
681 value
= strtok_r(NULL
, ", \t\r\n", &save_ptr
);
683 return xasprintf("field %s missing value", name
);
686 if (!strcmp(name
, "type")) {
687 /* Start a new band */
688 band
= ofpbuf_put_zeros(bands
, sizeof *band
);
691 if (!strcmp(value
, "drop")) {
692 band
->type
= OFPMBT13_DROP
;
693 } else if (!strcmp(value
, "dscp_remark")) {
694 band
->type
= OFPMBT13_DSCP_REMARK
;
696 return xasprintf("field %s unknown value %s", name
, value
);
698 } else if (!band
|| !band
->type
) {
699 return xstrdup("band must start with the 'type' keyword");
700 } else if (!strcmp(name
, "rate")) {
701 char *error
= str_to_u32(value
, &band
->rate
);
705 } else if (!strcmp(name
, "burst_size")) {
706 char *error
= str_to_u32(value
, &band
->burst_size
);
710 } else if (!strcmp(name
, "prec_level")) {
711 char *error
= str_to_u8(value
, name
, &band
->prec_level
);
716 return xasprintf("unknown keyword %s", name
);
721 return xstrdup("meter must have bands");
724 mm
->meter
.n_bands
= n_bands
;
725 mm
->meter
.bands
= ofpbuf_steal_data(bands
);
727 for (i
= 0; i
< n_bands
; ++i
) {
728 band
= &mm
->meter
.bands
[i
];
731 return xstrdup("band must have 'type'");
733 if (band
->type
== OFPMBT13_DSCP_REMARK
) {
734 if (!band
->prec_level
) {
735 return xstrdup("'dscp_remark' band must have"
739 if (band
->prec_level
) {
740 return xstrdup("Only 'dscp_remark' band may have"
745 return xstrdup("band must have 'rate'");
747 if (mm
->meter
.flags
& OFPMF13_BURST
) {
748 if (!band
->burst_size
) {
749 return xstrdup("band must have 'burst_size' "
750 "when 'burst' flag is set");
753 if (band
->burst_size
) {
754 return xstrdup("band may have 'burst_size' only "
755 "when 'burst' flag is set");
764 /* Convert 'str_' (as described in the Meter Syntax section of the
765 * ovs-ofctl man page) into 'mm' for sending the specified meter_mod
766 * 'command' to a switch.
768 * Returns NULL if successful, otherwise a malloc()'d string describing the
769 * error. The caller is responsible for freeing the returned string.
770 * If successful, 'mm->meter.bands' must be free()'d by the caller. */
771 char * OVS_WARN_UNUSED_RESULT
772 parse_ofp_meter_mod_str(struct ofputil_meter_mod
*mm
, const char *str_
,
773 int command
, enum ofputil_protocol
*usable_protocols
)
779 ofpbuf_init(&bands
, 64);
780 string
= xstrdup(str_
);
782 error
= parse_ofp_meter_mod_str__(mm
, string
, &bands
, command
,
786 ofpbuf_uninit(&bands
);
792 ofputil_format_meter_mod(struct ds
*s
, const struct ofputil_meter_mod
*mm
)
794 switch (mm
->command
) {
796 ds_put_cstr(s
, " ADD ");
799 ds_put_cstr(s
, " MOD ");
802 ds_put_cstr(s
, " DEL ");
805 ds_put_format(s
, " cmd:%d ", mm
->command
);
808 ofputil_format_meter_config(s
, &mm
->meter
);