]> git.proxmox.com Git - mirror_ovs.git/blame - lib/ofp-meter.c
ovsdb-idl: Fix iteration over tracked rows with no actual data.
[mirror_ovs.git] / lib / ofp-meter.c
CommitLineData
0d71302e
BP
1/*
2 * Copyright (c) 2008-2017 Nicira, Inc.
3 *
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:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#include <config.h>
18#include "openvswitch/ofp-meter.h"
19#include "byte-order.h"
20#include "nx-match.h"
21#include "openvswitch/ofp-errors.h"
22#include "openvswitch/ofp-msgs.h"
23#include "openvswitch/ofp-parse.h"
fe2c69f4 24#include "openvswitch/ofp-print.h"
0d71302e
BP
25#include "openvswitch/ofpbuf.h"
26#include "openvswitch/vlog.h"
27
28VLOG_DEFINE_THIS_MODULE(ofp_meter);
29
30static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
31
fe2c69f4
BP
32void
33ofputil_format_meter_id(struct ds *s, uint32_t meter_id, char separator)
34{
35 if (meter_id <= OFPM13_MAX) {
36 ds_put_format(s, "meter%c%"PRIu32, separator, meter_id);
37 } else {
38 const char *name;
39 switch (meter_id) {
40 case OFPM13_SLOWPATH:
41 name = "slowpath";
42 break;
43 case OFPM13_CONTROLLER:
44 name = "controller";
45 break;
46 case OFPM13_ALL:
47 name = "all";
48 break;
49 default:
50 name = "unknown";
51 }
52 ds_put_format(s, "meter%c%s", separator, name);
53 }
54}
55
56void
57ofputil_format_meter_band(struct ds *s, enum ofp13_meter_flags flags,
58 const struct ofputil_meter_band *mb)
59{
60 ds_put_cstr(s, "\ntype=");
61 switch (mb->type) {
62 case OFPMBT13_DROP:
63 ds_put_cstr(s, "drop");
64 break;
65 case OFPMBT13_DSCP_REMARK:
66 ds_put_cstr(s, "dscp_remark");
67 break;
68 default:
69 ds_put_format(s, "%u", mb->type);
70 }
71
72 ds_put_format(s, " rate=%"PRIu32, mb->rate);
73
74 if (flags & OFPMF13_BURST) {
75 ds_put_format(s, " burst_size=%"PRIu32, mb->burst_size);
76 }
77 if (mb->type == OFPMBT13_DSCP_REMARK) {
78 ds_put_format(s, " prec_level=%"PRIu8, mb->prec_level);
79 }
80}
81
0d71302e
BP
82static enum ofperr
83ofputil_pull_bands(struct ofpbuf *msg, size_t len, uint16_t *n_bands,
84 struct ofpbuf *bands)
85{
86 const struct ofp13_meter_band_header *ombh;
87 struct ofputil_meter_band *mb;
88 uint16_t n = 0;
89
90 ombh = ofpbuf_try_pull(msg, len);
91 if (!ombh) {
92 return OFPERR_OFPBRC_BAD_LEN;
93 }
94
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;
100 }
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;
105 }
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;
110 n++;
111 len -= ombh_len;
112 ombh = ALIGNED_CAST(struct ofp13_meter_band_header *,
113 (char *) ombh + ombh_len);
114 }
115 if (len) {
116 return OFPERR_OFPBRC_BAD_LEN;
117 }
118 *n_bands = n;
119 return 0;
120}
121
122enum ofperr
123ofputil_decode_meter_mod(const struct ofp_header *oh,
124 struct ofputil_meter_mod *mm,
125 struct ofpbuf *bands)
126{
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);
130
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;
137 }
138 mm->meter.meter_id = ntohl(omm->meter_id);
139
140 if (mm->command == OFPMC13_DELETE) {
141 mm->meter.flags = 0;
142 mm->meter.n_bands = 0;
143 mm->meter.bands = NULL;
144 } else {
145 enum ofperr error;
146
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;
151 }
0d71302e
BP
152
153 error = ofputil_pull_bands(&b, b.size, &mm->meter.n_bands, bands);
154 if (error) {
155 return error;
156 }
8b70d824 157 mm->meter.bands = bands->data;
0d71302e
BP
158 }
159 return 0;
160}
161
162void
163ofputil_decode_meter_request(const struct ofp_header *oh, uint32_t *meter_id)
164{
165 const struct ofp13_meter_multipart_request *omr = ofpmsg_body(oh);
166 *meter_id = ntohl(omr->meter_id);
167}
168
169struct ofpbuf *
170ofputil_encode_meter_request(enum ofp_version ofp_version,
171 enum ofputil_meter_request_type type,
172 uint32_t meter_id)
173{
174 struct ofpbuf *msg;
175
176 enum ofpraw raw;
177
178 switch (type) {
179 case OFPUTIL_METER_CONFIG:
180 raw = OFPRAW_OFPST13_METER_CONFIG_REQUEST;
181 break;
182 case OFPUTIL_METER_STATS:
183 raw = OFPRAW_OFPST13_METER_REQUEST;
184 break;
185 default:
186 case OFPUTIL_METER_FEATURES:
187 raw = OFPRAW_OFPST13_METER_FEATURES_REQUEST;
188 break;
189 }
190
191 msg = ofpraw_alloc(raw, ofp_version, 0);
192
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);
197 }
198 return msg;
199}
200
201static void
202ofputil_put_bands(uint16_t n_bands, const struct ofputil_meter_band *mb,
203 struct ofpbuf *msg)
204{
205 uint16_t n = 0;
206
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;
211
212 ombh = ofpbuf_put_zeros(msg, ombh_len);
213
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;
219
220 mb++;
221 }
222}
223
224/* Encode a meter stat for 'mc' and append it to 'replies'. */
225void
226ofputil_append_meter_config(struct ovs_list *replies,
227 const struct ofputil_meter_config *mc)
228{
229 struct ofpbuf *msg = ofpbuf_from_list(ovs_list_back(replies));
230 size_t start_ofs = msg->size;
231 struct ofp13_meter_config *reply;
232
233 ofpbuf_put_uninit(msg, sizeof *reply);
234 ofputil_put_bands(mc->n_bands, mc->bands, msg);
235
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);
240
241 ofpmp_postappend(replies, start_ofs);
242}
243
244/* Encode a meter stat for 'ms' and append it to 'replies'. */
245void
246ofputil_append_meter_stats(struct ovs_list *replies,
247 const struct ofputil_meter_stats *ms)
248{
249 struct ofp13_meter_stats *reply;
250 uint16_t n = 0;
251 uint16_t len;
252
253 len = sizeof *reply + ms->n_bands * sizeof(struct ofp13_meter_band_stats);
254 reply = ofpmp_append(replies, len);
255
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);
264
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];
268
269 dst->packet_band_count = htonll(src->packet_count);
270 dst->byte_band_count = htonll(src->byte_count);
271 }
272}
273
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.
278 *
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.
282 *
283 * Returns 0 if successful, EOF if no replies were left in this 'msg',
284 * otherwise a positive errno value. */
285int
286ofputil_decode_meter_config(struct ofpbuf *msg,
287 struct ofputil_meter_config *mc,
288 struct ofpbuf *bands)
289{
290 const struct ofp13_meter_config *omc;
291 enum ofperr err;
292
293 /* Pull OpenFlow headers for the first call. */
294 if (!msg->header) {
295 ofpraw_pull_assert(msg);
296 }
297
298 if (!msg->size) {
299 return EOF;
300 }
301
302 omc = ofpbuf_try_pull(msg, sizeof *omc);
303 if (!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;
307 }
308
309 ofpbuf_clear(bands);
310 err = ofputil_pull_bands(msg, ntohs(omc->length) - sizeof *omc,
311 &mc->n_bands, bands);
312 if (err) {
313 return err;
314 }
315 mc->meter_id = ntohl(omc->meter_id);
316 mc->flags = ntohs(omc->flags);
317 mc->bands = bands->data;
318
319 return 0;
320}
321
fe2c69f4
BP
322static void
323ofp_print_meter_flags(struct ds *s, enum ofp13_meter_flags flags)
324{
325 if (flags & OFPMF13_KBPS) {
326 ds_put_cstr(s, "kbps ");
327 }
328 if (flags & OFPMF13_PKTPS) {
329 ds_put_cstr(s, "pktps ");
330 }
331 if (flags & OFPMF13_BURST) {
332 ds_put_cstr(s, "burst ");
333 }
334 if (flags & OFPMF13_STATS) {
335 ds_put_cstr(s, "stats ");
336 }
337
338 flags &= ~(OFPMF13_KBPS | OFPMF13_PKTPS | OFPMF13_BURST | OFPMF13_STATS);
339 if (flags) {
e4614813 340 ds_put_format(s, "flags:0x%x ", (unsigned)flags);
fe2c69f4
BP
341 }
342}
343
344void
345ofputil_format_meter_config(struct ds *s,
346 const struct ofputil_meter_config *mc)
347{
348 uint16_t i;
349
350 ofputil_format_meter_id(s, mc->meter_id, '=');
351 ds_put_char(s, ' ');
352
353 ofp_print_meter_flags(s, mc->flags);
354
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]);
358 }
359 ds_put_char(s, '\n');
360}
361
0d71302e
BP
362static enum ofperr
363ofputil_pull_band_stats(struct ofpbuf *msg, size_t len, uint16_t *n_bands,
364 struct ofpbuf *bands)
365{
366 const struct ofp13_meter_band_stats *ombs;
367 struct ofputil_meter_band_stats *mbs;
368 uint16_t n, i;
369
370 ombs = ofpbuf_try_pull(msg, len);
371 if (!ombs) {
372 return OFPERR_OFPBRC_BAD_LEN;
373 }
374
375 n = len / sizeof *ombs;
376 if (len != n * sizeof *ombs) {
377 return OFPERR_OFPBRC_BAD_LEN;
378 }
379
380 mbs = ofpbuf_put_uninit(bands, len);
381
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);
385 }
386 *n_bands = n;
387 return 0;
388}
389
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'.
393 *
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.
397 *
398 * Returns 0 if successful, EOF if no replies were left in this 'msg',
399 * otherwise a positive errno value. */
400int
401ofputil_decode_meter_stats(struct ofpbuf *msg,
402 struct ofputil_meter_stats *ms,
403 struct ofpbuf *bands)
404{
405 const struct ofp13_meter_stats *oms;
406 enum ofperr err;
407
408 /* Pull OpenFlow headers for the first call. */
409 if (!msg->header) {
410 ofpraw_pull_assert(msg);
411 }
412
413 if (!msg->size) {
414 return EOF;
415 }
416
417 oms = ofpbuf_try_pull(msg, sizeof *oms);
418 if (!oms) {
419 VLOG_WARN_RL(&rl, "OFPMP_METER reply has %"PRIu32" leftover bytes "
420 "at end", msg->size);
421 return OFPERR_OFPBRC_BAD_LEN;
422 }
423
424 ofpbuf_clear(bands);
425 err = ofputil_pull_band_stats(msg, ntohs(oms->len) - sizeof *oms,
426 &ms->n_bands, bands);
427 if (err) {
428 return err;
429 }
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;
437
438 return 0;
439}
440
fe2c69f4
BP
441void
442ofputil_format_meter_stats(struct ds *s, const struct ofputil_meter_stats *ms)
443{
444 uint16_t i;
445
446 ofputil_format_meter_id(s, ms->meter_id, ':');
447 ds_put_char(s, ' ');
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);
453 ds_put_char(s, ' ');
454
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);
460 }
461}
462
0d71302e
BP
463void
464ofputil_decode_meter_features(const struct ofp_header *oh,
465 struct ofputil_meter_features *mf)
466{
467 const struct ofp13_meter_features *omf = ofpmsg_body(oh);
468
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;
474}
475
476struct ofpbuf *
477ofputil_encode_meter_features_reply(const struct ofputil_meter_features *mf,
478 const struct ofp_header *request)
479{
480 struct ofpbuf *reply;
481 struct ofp13_meter_features *omf;
482
483 reply = ofpraw_alloc_stats_reply(request, 0);
484 omf = ofpbuf_put_zeros(reply, sizeof *omf);
485
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;
491
492 return reply;
493}
494
fe2c69f4
BP
495static const char *
496ofputil_meter_band_types_to_name(uint32_t bit)
497{
498 switch (bit) {
499 case 1 << OFPMBT13_DROP: return "drop";
500 case 1 << OFPMBT13_DSCP_REMARK: return "dscp_remark";
501 }
502
503 return NULL;
504}
505
506static const char *
507ofputil_meter_capabilities_to_name(uint32_t bit)
508{
509 enum ofp13_meter_flags flag = bit;
510
511 switch (flag) {
512 case OFPMF13_KBPS: return "kbps";
513 case OFPMF13_PKTPS: return "pktps";
514 case OFPMF13_BURST: return "burst";
515 case OFPMF13_STATS: return "stats";
516 }
517
518 return NULL;
519}
520
521void
522ofputil_format_meter_features(struct ds *s,
523 const struct ofputil_meter_features *mf)
524{
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);
528
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');
533
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');
538}
539
0d71302e
BP
540struct ofpbuf *
541ofputil_encode_meter_mod(enum ofp_version ofp_version,
542 const struct ofputil_meter_mod *mm)
543{
544 struct ofpbuf *msg;
545
546 struct ofp13_meter_mod *omm;
547
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);
554 }
555 omm->meter_id = htonl(mm->meter.meter_id);
556
557 ofputil_put_bands(mm->meter.n_bands, mm->meter.bands, msg);
558
559 ofpmsg_update_length(msg);
560 return msg;
561}
562
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. */
565static char * OVS_WARN_UNUSED_RESULT
566parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string,
567 struct ofpbuf *bands, int command,
568 enum ofputil_protocol *usable_protocols)
569{
570 enum {
571 F_METER = 1 << 0,
572 F_FLAGS = 1 << 1,
573 F_BANDS = 1 << 2,
574 } fields;
575 char *save_ptr = NULL;
576 char *band_str = NULL;
577 char *name;
578
579 /* Meters require at least OF 1.3. */
580 *usable_protocols = OFPUTIL_P_OF13_UP;
581
582 switch (command) {
583 case -1:
584 fields = F_METER;
585 break;
586
587 case OFPMC13_ADD:
588 fields = F_METER | F_FLAGS | F_BANDS;
589 break;
590
591 case OFPMC13_DELETE:
592 fields = F_METER;
593 break;
594
595 case OFPMC13_MODIFY:
596 fields = F_METER | F_FLAGS | F_BANDS;
597 break;
598
599 default:
600 OVS_NOT_REACHED();
601 }
602
603 mm->command = command;
604 mm->meter.meter_id = 0;
605 mm->meter.flags = 0;
606 mm->meter.n_bands = 0;
607 mm->meter.bands = NULL;
608
609 if (fields & F_BANDS) {
610 band_str = strstr(string, "band");
611 if (!band_str) {
612 return xstrdup("must specify bands");
613 }
614 *band_str = '\0';
615
616 band_str = strchr(band_str + 1, '=');
617 if (!band_str) {
618 return xstrdup("must specify bands");
619 }
620
621 band_str++;
622 }
623 for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
624 name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
625
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;
634 } else {
635 char *value;
636
637 value = strtok_r(NULL, ", \t\r\n", &save_ptr);
638 if (!value) {
639 return xasprintf("field %s missing value", name);
640 }
641
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;
649 } else {
650 char *error = str_to_u32(value, &mm->meter.meter_id);
651 if (error) {
652 return error;
653 }
654 if (mm->meter.meter_id > OFPM13_MAX
655 || !mm->meter.meter_id) {
656 return xasprintf("invalid value for %s", name);
657 }
658 }
659 } else {
660 return xasprintf("unknown keyword %s", name);
661 }
662 }
663 }
664 if (fields & F_METER && !mm->meter.meter_id) {
665 return xstrdup("must specify 'meter'");
666 }
667 if (fields & F_FLAGS && !mm->meter.flags) {
668 return xstrdup("meter must specify either 'kbps' or 'pktps'");
669 }
670
671 if (fields & F_BANDS) {
672 uint16_t n_bands = 0;
673 struct ofputil_meter_band *band = NULL;
674 int i;
675
676 for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name;
677 name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
678
679 char *value;
680
681 value = strtok_r(NULL, ", \t\r\n", &save_ptr);
682 if (!value) {
683 return xasprintf("field %s missing value", name);
684 }
685
686 if (!strcmp(name, "type")) {
687 /* Start a new band */
688 band = ofpbuf_put_zeros(bands, sizeof *band);
689 n_bands++;
690
691 if (!strcmp(value, "drop")) {
692 band->type = OFPMBT13_DROP;
693 } else if (!strcmp(value, "dscp_remark")) {
694 band->type = OFPMBT13_DSCP_REMARK;
695 } else {
696 return xasprintf("field %s unknown value %s", name, value);
697 }
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);
702 if (error) {
703 return error;
704 }
705 } else if (!strcmp(name, "burst_size")) {
706 char *error = str_to_u32(value, &band->burst_size);
707 if (error) {
708 return error;
709 }
710 } else if (!strcmp(name, "prec_level")) {
711 char *error = str_to_u8(value, name, &band->prec_level);
712 if (error) {
713 return error;
714 }
715 } else {
716 return xasprintf("unknown keyword %s", name);
717 }
718 }
719 /* validate bands */
720 if (!n_bands) {
721 return xstrdup("meter must have bands");
722 }
723
724 mm->meter.n_bands = n_bands;
725 mm->meter.bands = ofpbuf_steal_data(bands);
726
727 for (i = 0; i < n_bands; ++i) {
728 band = &mm->meter.bands[i];
729
730 if (!band->type) {
731 return xstrdup("band must have 'type'");
732 }
733 if (band->type == OFPMBT13_DSCP_REMARK) {
734 if (!band->prec_level) {
735 return xstrdup("'dscp_remark' band must have"
736 " 'prec_level'");
737 }
738 } else {
739 if (band->prec_level) {
740 return xstrdup("Only 'dscp_remark' band may have"
741 " 'prec_level'");
742 }
743 }
744 if (!band->rate) {
745 return xstrdup("band must have 'rate'");
746 }
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");
751 }
752 } else {
753 if (band->burst_size) {
754 return xstrdup("band may have 'burst_size' only "
755 "when 'burst' flag is set");
756 }
757 }
758 }
759 }
760
761 return NULL;
762}
763
5d2988f7
JP
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.
0d71302e
BP
767 *
768 * Returns NULL if successful, otherwise a malloc()'d string describing the
769 * error. The caller is responsible for freeing the returned string.
5d2988f7 770 * If successful, 'mm->meter.bands' must be free()'d by the caller. */
0d71302e
BP
771char * OVS_WARN_UNUSED_RESULT
772parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
773 int command, enum ofputil_protocol *usable_protocols)
774{
775 struct ofpbuf bands;
776 char *string;
777 char *error;
778
779 ofpbuf_init(&bands, 64);
780 string = xstrdup(str_);
781
782 error = parse_ofp_meter_mod_str__(mm, string, &bands, command,
783 usable_protocols);
784
785 free(string);
786 ofpbuf_uninit(&bands);
787
788 return error;
789}
fe2c69f4
BP
790
791void
792ofputil_format_meter_mod(struct ds *s, const struct ofputil_meter_mod *mm)
793{
794 switch (mm->command) {
795 case OFPMC13_ADD:
796 ds_put_cstr(s, " ADD ");
797 break;
798 case OFPMC13_MODIFY:
799 ds_put_cstr(s, " MOD ");
800 break;
801 case OFPMC13_DELETE:
802 ds_put_cstr(s, " DEL ");
803 break;
804 default:
805 ds_put_format(s, " cmd:%d ", mm->command);
806 }
807
808 ofputil_format_meter_config(s, &mm->meter);
809}