Coverage Report

Created: 2025-07-18 06:07

/src/openvswitch/lib/ofp-meter.c
Line
Count
Source (jump to first uncovered line)
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"
24
#include "openvswitch/ofp-print.h"
25
#include "openvswitch/ofpbuf.h"
26
#include "openvswitch/vlog.h"
27
28
VLOG_DEFINE_THIS_MODULE(ofp_meter);
29
30
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
31
32
void
33
ofputil_format_meter_id(struct ds *s, uint32_t meter_id, char separator)
34
0
{
35
0
    if (meter_id <= OFPM13_MAX) {
36
0
        ds_put_format(s, "meter%c%"PRIu32, separator, meter_id);
37
0
    } else {
38
0
        const char *name;
39
0
        switch (meter_id) {
40
0
        case OFPM13_SLOWPATH:
41
0
            name = "slowpath";
42
0
            break;
43
0
        case OFPM13_CONTROLLER:
44
0
            name = "controller";
45
0
            break;
46
0
        case OFPM13_ALL:
47
0
            name = "all";
48
0
            break;
49
0
        default:
50
0
            name = "unknown";
51
0
        }
52
0
        ds_put_format(s, "meter%c%s", separator, name);
53
0
    }
54
0
}
55
56
void
57
ofputil_format_meter_band(struct ds *s, enum ofp13_meter_flags flags,
58
                          const struct ofputil_meter_band *mb)
59
0
{
60
0
    ds_put_cstr(s, "\ntype=");
61
0
    switch (mb->type) {
62
0
    case OFPMBT13_DROP:
63
0
        ds_put_cstr(s, "drop");
64
0
        break;
65
0
    case OFPMBT13_DSCP_REMARK:
66
0
        ds_put_cstr(s, "dscp_remark");
67
0
        break;
68
0
    default:
69
0
        ds_put_format(s, "%u", mb->type);
70
0
    }
71
72
0
    ds_put_format(s, " rate=%"PRIu32, mb->rate);
73
74
0
    if (flags & OFPMF13_BURST) {
75
0
        ds_put_format(s, " burst_size=%"PRIu32, mb->burst_size);
76
0
    }
77
0
    if (mb->type == OFPMBT13_DSCP_REMARK) {
78
0
        ds_put_format(s, " prec_level=%"PRIu8, mb->prec_level);
79
0
    }
80
0
}
81
82
static enum ofperr
83
ofputil_pull_bands(struct ofpbuf *msg, size_t len, uint16_t *n_bands,
84
                   struct ofpbuf *bands)
85
0
{
86
0
    const struct ofp13_meter_band_header *ombh;
87
0
    struct ofputil_meter_band *mb;
88
0
    uint16_t n = 0;
89
90
0
    ombh = ofpbuf_try_pull(msg, len);
91
0
    if (!ombh) {
92
0
        return OFPERR_OFPBRC_BAD_LEN;
93
0
    }
94
95
0
    while (len >= sizeof (struct ofp13_meter_band_drop)) {
96
0
        size_t ombh_len = ntohs(ombh->len);
97
        /* All supported band types have the same length. */
98
0
        if (ombh_len != sizeof (struct ofp13_meter_band_drop)) {
99
0
            return OFPERR_OFPBRC_BAD_LEN;
100
0
        }
101
0
        mb = ofpbuf_put_uninit(bands, sizeof *mb);
102
0
        mb->type = ntohs(ombh->type);
103
0
        if (mb->type != OFPMBT13_DROP && mb->type != OFPMBT13_DSCP_REMARK) {
104
0
            return OFPERR_OFPMMFC_BAD_BAND;
105
0
        }
106
0
        mb->rate = ntohl(ombh->rate);
107
0
        mb->burst_size = ntohl(ombh->burst_size);
108
0
        mb->prec_level = (mb->type == OFPMBT13_DSCP_REMARK) ?
109
0
            ((struct ofp13_meter_band_dscp_remark *)ombh)->prec_level : 0;
110
0
        n++;
111
0
        len -= ombh_len;
112
0
        ombh = ALIGNED_CAST(struct ofp13_meter_band_header *,
113
0
                            (char *) ombh + ombh_len);
114
0
    }
115
0
    if (len) {
116
0
        return OFPERR_OFPBRC_BAD_LEN;
117
0
    }
118
0
    *n_bands = n;
119
0
    return 0;
120
0
}
121
122
enum ofperr
123
ofputil_decode_meter_mod(const struct ofp_header *oh,
124
                         struct ofputil_meter_mod *mm,
125
                         struct ofpbuf *bands)
126
0
{
127
0
    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
128
0
    ofpraw_pull_assert(&b);
129
0
    const struct ofp13_meter_mod *omm = ofpbuf_pull(&b, sizeof *omm);
130
131
    /* Translate the message. */
132
0
    mm->command = ntohs(omm->command);
133
0
    if (mm->command != OFPMC13_ADD &&
134
0
        mm->command != OFPMC13_MODIFY &&
135
0
        mm->command != OFPMC13_DELETE) {
136
0
        return OFPERR_OFPMMFC_BAD_COMMAND;
137
0
    }
138
0
    mm->meter.meter_id = ntohl(omm->meter_id);
139
140
0
    if (mm->command == OFPMC13_DELETE) {
141
0
        mm->meter.flags = 0;
142
0
        mm->meter.n_bands = 0;
143
0
        mm->meter.bands = NULL;
144
0
    } else {
145
0
        enum ofperr error;
146
147
0
        mm->meter.flags = ntohs(omm->flags);
148
0
        if (mm->meter.flags & OFPMF13_KBPS &&
149
0
            mm->meter.flags & OFPMF13_PKTPS) {
150
0
            return OFPERR_OFPMMFC_BAD_FLAGS;
151
0
        }
152
153
0
        error = ofputil_pull_bands(&b, b.size, &mm->meter.n_bands, bands);
154
0
        if (error) {
155
0
            return error;
156
0
        }
157
0
        mm->meter.bands = bands->data;
158
0
    }
159
0
    return 0;
160
0
}
161
162
void
163
ofputil_decode_meter_request(const struct ofp_header *oh, uint32_t *meter_id)
164
0
{
165
0
    const struct ofp13_meter_multipart_request *omr = ofpmsg_body(oh);
166
0
    *meter_id = ntohl(omr->meter_id);
167
0
}
168
169
struct ofpbuf *
170
ofputil_encode_meter_request(enum ofp_version ofp_version,
171
                             enum ofputil_meter_request_type type,
172
                             uint32_t meter_id)
173
0
{
174
0
    struct ofpbuf *msg;
175
176
0
    enum ofpraw raw;
177
178
0
    switch (type) {
179
0
    case OFPUTIL_METER_CONFIG:
180
0
        raw = OFPRAW_OFPST13_METER_CONFIG_REQUEST;
181
0
        break;
182
0
    case OFPUTIL_METER_STATS:
183
0
        raw = OFPRAW_OFPST13_METER_REQUEST;
184
0
        break;
185
0
    default:
186
0
    case OFPUTIL_METER_FEATURES:
187
0
        raw = OFPRAW_OFPST13_METER_FEATURES_REQUEST;
188
0
        break;
189
0
    }
190
191
0
    msg = ofpraw_alloc(raw, ofp_version, 0);
192
193
0
    if (type != OFPUTIL_METER_FEATURES) {
194
0
        struct ofp13_meter_multipart_request *omr;
195
0
        omr = ofpbuf_put_zeros(msg, sizeof *omr);
196
0
        omr->meter_id = htonl(meter_id);
197
0
    }
198
0
    return msg;
199
0
}
200
201
static void
202
ofputil_put_bands(uint16_t n_bands, const struct ofputil_meter_band *mb,
203
                  struct ofpbuf *msg)
204
0
{
205
0
    uint16_t n = 0;
206
207
0
    for (n = 0; n < n_bands; ++n) {
208
        /* Currently all band types have same size. */
209
0
        struct ofp13_meter_band_dscp_remark *ombh;
210
0
        size_t ombh_len = sizeof *ombh;
211
212
0
        ombh = ofpbuf_put_zeros(msg, ombh_len);
213
214
0
        ombh->type = htons(mb->type);
215
0
        ombh->len = htons(ombh_len);
216
0
        ombh->rate = htonl(mb->rate);
217
0
        ombh->burst_size = htonl(mb->burst_size);
218
0
        ombh->prec_level = mb->prec_level;
219
220
0
        mb++;
221
0
    }
222
0
}
223
224
/* Encode a meter stat for 'mc' and append it to 'replies'. */
225
void
226
ofputil_append_meter_config(struct ovs_list *replies,
227
                            const struct ofputil_meter_config *mc)
228
0
{
229
0
    struct ofpbuf *msg = ofpbuf_from_list(ovs_list_back(replies));
230
0
    size_t start_ofs = msg->size;
231
0
    struct ofp13_meter_config *reply;
232
233
0
    ofpbuf_put_uninit(msg, sizeof *reply);
234
0
    ofputil_put_bands(mc->n_bands, mc->bands, msg);
235
236
0
    reply = ofpbuf_at_assert(msg, start_ofs, sizeof *reply);
237
0
    reply->flags = htons(mc->flags);
238
0
    reply->meter_id = htonl(mc->meter_id);
239
0
    reply->length = htons(msg->size - start_ofs);
240
241
0
    ofpmp_postappend(replies, start_ofs);
242
0
}
243
244
/* Encode a meter stat for 'ms' and append it to 'replies'. */
245
void
246
ofputil_append_meter_stats(struct ovs_list *replies,
247
                           const struct ofputil_meter_stats *ms)
248
0
{
249
0
    struct ofp13_meter_stats *reply;
250
0
    uint16_t n = 0;
251
0
    uint16_t len;
252
253
0
    len = sizeof *reply + ms->n_bands * sizeof(struct ofp13_meter_band_stats);
254
0
    reply = ofpmp_append(replies, len);
255
256
0
    reply->meter_id = htonl(ms->meter_id);
257
0
    reply->len = htons(len);
258
0
    memset(reply->pad, 0, sizeof reply->pad);
259
0
    reply->flow_count = htonl(ms->flow_count);
260
0
    reply->packet_in_count = htonll(ms->packet_in_count);
261
0
    reply->byte_in_count = htonll(ms->byte_in_count);
262
0
    reply->duration_sec = htonl(ms->duration_sec);
263
0
    reply->duration_nsec = htonl(ms->duration_nsec);
264
265
0
    for (n = 0; n < ms->n_bands; ++n) {
266
0
        const struct ofputil_meter_band_stats *src = &ms->bands[n];
267
0
        struct ofp13_meter_band_stats *dst = &reply->band_stats[n];
268
269
0
        dst->packet_band_count = htonll(src->packet_count);
270
0
        dst->byte_band_count = htonll(src->byte_count);
271
0
    }
272
0
}
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. */
285
int
286
ofputil_decode_meter_config(struct ofpbuf *msg,
287
                            struct ofputil_meter_config *mc,
288
                            struct ofpbuf *bands)
289
0
{
290
0
    const struct ofp13_meter_config *omc;
291
0
    enum ofperr err;
292
293
    /* Pull OpenFlow headers for the first call. */
294
0
    if (!msg->header) {
295
0
        ofpraw_pull_assert(msg);
296
0
    }
297
298
0
    if (!msg->size) {
299
0
        return EOF;
300
0
    }
301
302
0
    omc = ofpbuf_try_pull(msg, sizeof *omc);
303
0
    if (!omc) {
304
0
        VLOG_WARN_RL(&rl, "OFPMP_METER_CONFIG reply has %"PRIu32" leftover "
305
0
                     "bytes at end", msg->size);
306
0
        return OFPERR_OFPBRC_BAD_LEN;
307
0
    }
308
309
0
    ofpbuf_clear(bands);
310
0
    err = ofputil_pull_bands(msg, ntohs(omc->length) - sizeof *omc,
311
0
                             &mc->n_bands, bands);
312
0
    if (err) {
313
0
        return err;
314
0
    }
315
0
    mc->meter_id = ntohl(omc->meter_id);
316
0
    mc->flags = ntohs(omc->flags);
317
0
    mc->bands = bands->data;
318
319
0
    return 0;
320
0
}
321
322
static void
323
ofp_print_meter_flags(struct ds *s, enum ofp13_meter_flags flags)
324
0
{
325
0
    if (flags & OFPMF13_KBPS) {
326
0
        ds_put_cstr(s, "kbps ");
327
0
    }
328
0
    if (flags & OFPMF13_PKTPS) {
329
0
        ds_put_cstr(s, "pktps ");
330
0
    }
331
0
    if (flags & OFPMF13_BURST) {
332
0
        ds_put_cstr(s, "burst ");
333
0
    }
334
0
    if (flags & OFPMF13_STATS) {
335
0
        ds_put_cstr(s, "stats ");
336
0
    }
337
338
0
    flags &= ~(OFPMF13_KBPS | OFPMF13_PKTPS | OFPMF13_BURST | OFPMF13_STATS);
339
0
    if (flags) {
340
0
        ds_put_format(s, "flags:0x%x ", (unsigned)flags);
341
0
    }
342
0
}
343
344
void
345
ofputil_format_meter_config(struct ds *s,
346
                            const struct ofputil_meter_config *mc)
347
0
{
348
0
    uint16_t i;
349
350
0
    ofputil_format_meter_id(s, mc->meter_id, '=');
351
0
    ds_put_char(s, ' ');
352
353
0
    ofp_print_meter_flags(s, mc->flags);
354
355
0
    ds_put_cstr(s, "bands=");
356
0
    for (i = 0; i < mc->n_bands; ++i) {
357
0
        ofputil_format_meter_band(s, mc->flags, &mc->bands[i]);
358
0
    }
359
0
    ds_put_char(s, '\n');
360
0
}
361
362
static enum ofperr
363
ofputil_pull_band_stats(struct ofpbuf *msg, size_t len, uint16_t *n_bands,
364
                        struct ofpbuf *bands)
365
0
{
366
0
    const struct ofp13_meter_band_stats *ombs;
367
0
    struct ofputil_meter_band_stats *mbs;
368
0
    uint16_t n, i;
369
370
0
    ombs = ofpbuf_try_pull(msg, len);
371
0
    if (!ombs) {
372
0
        return OFPERR_OFPBRC_BAD_LEN;
373
0
    }
374
375
0
    n = len / sizeof *ombs;
376
0
    if (len != n * sizeof *ombs) {
377
0
        return OFPERR_OFPBRC_BAD_LEN;
378
0
    }
379
380
0
    mbs = ofpbuf_put_uninit(bands, len);
381
382
0
    for (i = 0; i < n; ++i) {
383
0
        mbs[i].packet_count = ntohll(ombs[i].packet_band_count);
384
0
        mbs[i].byte_count = ntohll(ombs[i].byte_band_count);
385
0
    }
386
0
    *n_bands = n;
387
0
    return 0;
388
0
}
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. */
400
int
401
ofputil_decode_meter_stats(struct ofpbuf *msg,
402
                           struct ofputil_meter_stats *ms,
403
                           struct ofpbuf *bands)
404
0
{
405
0
    const struct ofp13_meter_stats *oms;
406
0
    enum ofperr err;
407
408
    /* Pull OpenFlow headers for the first call. */
409
0
    if (!msg->header) {
410
0
        ofpraw_pull_assert(msg);
411
0
    }
412
413
0
    if (!msg->size) {
414
0
        return EOF;
415
0
    }
416
417
0
    oms = ofpbuf_try_pull(msg, sizeof *oms);
418
0
    if (!oms) {
419
0
        VLOG_WARN_RL(&rl, "OFPMP_METER reply has %"PRIu32" leftover bytes "
420
0
                     "at end", msg->size);
421
0
        return OFPERR_OFPBRC_BAD_LEN;
422
0
    }
423
424
0
    ofpbuf_clear(bands);
425
0
    err = ofputil_pull_band_stats(msg, ntohs(oms->len) - sizeof *oms,
426
0
                                  &ms->n_bands, bands);
427
0
    if (err) {
428
0
        return err;
429
0
    }
430
0
    ms->meter_id = ntohl(oms->meter_id);
431
0
    ms->flow_count = ntohl(oms->flow_count);
432
0
    ms->packet_in_count = ntohll(oms->packet_in_count);
433
0
    ms->byte_in_count = ntohll(oms->byte_in_count);
434
0
    ms->duration_sec = ntohl(oms->duration_sec);
435
0
    ms->duration_nsec = ntohl(oms->duration_nsec);
436
0
    ms->bands = bands->data;
437
438
0
    return 0;
439
0
}
440
441
void
442
ofputil_format_meter_stats(struct ds *s, const struct ofputil_meter_stats *ms)
443
0
{
444
0
    uint16_t i;
445
446
0
    ofputil_format_meter_id(s, ms->meter_id, ':');
447
0
    ds_put_char(s, ' ');
448
0
    ds_put_format(s, "flow_count:%"PRIu32" ", ms->flow_count);
449
0
    ds_put_format(s, "packet_in_count:%"PRIu64" ", ms->packet_in_count);
450
0
    ds_put_format(s, "byte_in_count:%"PRIu64" ", ms->byte_in_count);
451
0
    ds_put_cstr(s, "duration:");
452
0
    ofp_print_duration(s, ms->duration_sec, ms->duration_nsec);
453
0
    ds_put_char(s, ' ');
454
455
0
    ds_put_cstr(s, "bands:\n");
456
0
    for (i = 0; i < ms->n_bands; ++i) {
457
0
        ds_put_format(s, "%d: ", i);
458
0
        ds_put_format(s, "packet_count:%"PRIu64" ", ms->bands[i].packet_count);
459
0
        ds_put_format(s, "byte_count:%"PRIu64"\n", ms->bands[i].byte_count);
460
0
    }
461
0
}
462
463
void
464
ofputil_decode_meter_features(const struct ofp_header *oh,
465
                              struct ofputil_meter_features *mf)
466
0
{
467
0
    const struct ofp13_meter_features *omf = ofpmsg_body(oh);
468
469
0
    mf->max_meters = ntohl(omf->max_meter);
470
0
    mf->band_types = ntohl(omf->band_types);
471
0
    mf->capabilities = ntohl(omf->capabilities);
472
0
    mf->max_bands = omf->max_bands;
473
0
    mf->max_color = omf->max_color;
474
0
}
475
476
struct ofpbuf *
477
ofputil_encode_meter_features_reply(const struct ofputil_meter_features *mf,
478
                                    const struct ofp_header *request)
479
0
{
480
0
    struct ofpbuf *reply;
481
0
    struct ofp13_meter_features *omf;
482
483
0
    reply = ofpraw_alloc_stats_reply(request, 0);
484
0
    omf = ofpbuf_put_zeros(reply, sizeof *omf);
485
486
0
    omf->max_meter = htonl(mf->max_meters);
487
0
    omf->band_types = htonl(mf->band_types);
488
0
    omf->capabilities = htonl(mf->capabilities);
489
0
    omf->max_bands = mf->max_bands;
490
0
    omf->max_color = mf->max_color;
491
492
0
    return reply;
493
0
}
494
495
static const char *
496
ofputil_meter_band_types_to_name(uint32_t bit)
497
0
{
498
0
    switch (bit) {
499
0
    case 1 << OFPMBT13_DROP:          return "drop";
500
0
    case 1 << OFPMBT13_DSCP_REMARK:   return "dscp_remark";
501
0
    }
502
503
0
    return NULL;
504
0
}
505
506
static const char *
507
ofputil_meter_capabilities_to_name(uint32_t bit)
508
0
{
509
0
    enum ofp13_meter_flags flag = bit;
510
511
0
    switch (flag) {
512
0
    case OFPMF13_KBPS:    return "kbps";
513
0
    case OFPMF13_PKTPS:   return "pktps";
514
0
    case OFPMF13_BURST:   return "burst";
515
0
    case OFPMF13_STATS:   return "stats";
516
0
    }
517
518
0
    return NULL;
519
0
}
520
521
void
522
ofputil_format_meter_features(struct ds *s,
523
                              const struct ofputil_meter_features *mf)
524
0
{
525
0
    ds_put_format(s, "\nmax_meter:%"PRIu32, mf->max_meters);
526
0
    ds_put_format(s, " max_bands:%"PRIu8, mf->max_bands);
527
0
    ds_put_format(s, " max_color:%"PRIu8"\n", mf->max_color);
528
529
0
    ds_put_cstr(s, "band_types: ");
530
0
    ofp_print_bit_names(s, mf->band_types,
531
0
                        ofputil_meter_band_types_to_name, ' ');
532
0
    ds_put_char(s, '\n');
533
534
0
    ds_put_cstr(s, "capabilities: ");
535
0
    ofp_print_bit_names(s, mf->capabilities,
536
0
                        ofputil_meter_capabilities_to_name, ' ');
537
0
    ds_put_char(s, '\n');
538
0
}
539
540
struct ofpbuf *
541
ofputil_encode_meter_mod(enum ofp_version ofp_version,
542
                         const struct ofputil_meter_mod *mm)
543
0
{
544
0
    struct ofpbuf *msg;
545
546
0
    struct ofp13_meter_mod *omm;
547
548
0
    msg = ofpraw_alloc(OFPRAW_OFPT13_METER_MOD, ofp_version,
549
0
                       NXM_TYPICAL_LEN + mm->meter.n_bands * 16);
550
0
    omm = ofpbuf_put_zeros(msg, sizeof *omm);
551
0
    omm->command = htons(mm->command);
552
0
    if (mm->command != OFPMC13_DELETE) {
553
0
        omm->flags = htons(mm->meter.flags);
554
0
    }
555
0
    omm->meter_id = htonl(mm->meter.meter_id);
556
557
0
    ofputil_put_bands(mm->meter.n_bands, mm->meter.bands, msg);
558
559
0
    ofpmsg_update_length(msg);
560
0
    return msg;
561
0
}
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. */
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)
569
0
{
570
0
    enum {
571
0
        F_METER = 1 << 0,
572
0
        F_FLAGS = 1 << 1,
573
0
        F_BANDS = 1 << 2,
574
0
    } fields;
575
0
    char *save_ptr = NULL;
576
0
    char *band_str = NULL;
577
0
    char *name;
578
579
    /* Meters require at least OF 1.3. */
580
0
    *usable_protocols = OFPUTIL_P_OF13_UP;
581
582
0
    switch (command) {
583
0
    case -1:
584
        /* This is a special case for requesting meters, which has no
585
         * specific command assigned.  To avoid compiler warnings, set
586
         * the command to UINT16_MAX. */
587
0
        fields = F_METER;
588
0
        command = UINT16_MAX;
589
0
        break;
590
591
0
    case OFPMC13_ADD:
592
0
        fields = F_METER | F_FLAGS | F_BANDS;
593
0
        break;
594
595
0
    case OFPMC13_DELETE:
596
0
        fields = F_METER;
597
0
        break;
598
599
0
    case OFPMC13_MODIFY:
600
0
        fields = F_METER | F_FLAGS | F_BANDS;
601
0
        break;
602
603
0
    default:
604
0
        OVS_NOT_REACHED();
605
0
    }
606
607
0
    mm->command = command;
608
0
    mm->meter.meter_id = 0;
609
0
    mm->meter.flags = 0;
610
0
    mm->meter.n_bands = 0;
611
0
    mm->meter.bands = NULL;
612
613
0
    if (fields & F_BANDS) {
614
0
        band_str = strstr(string, "band");
615
0
        if (!band_str) {
616
0
            return xstrdup("must specify bands");
617
0
        }
618
0
        *band_str = '\0';
619
620
0
        band_str = strchr(band_str + 1, '=');
621
0
        if (!band_str) {
622
0
            return xstrdup("must specify bands");
623
0
        }
624
625
0
        band_str++;
626
0
    }
627
0
    for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
628
0
         name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
629
630
0
        if (fields & F_FLAGS && !strcmp(name, "kbps")) {
631
0
            mm->meter.flags |= OFPMF13_KBPS;
632
0
        } else if (fields & F_FLAGS && !strcmp(name, "pktps")) {
633
0
            mm->meter.flags |= OFPMF13_PKTPS;
634
0
        } else if (fields & F_FLAGS && !strcmp(name, "burst")) {
635
0
            mm->meter.flags |= OFPMF13_BURST;
636
0
        } else if (fields & F_FLAGS && !strcmp(name, "stats")) {
637
0
            mm->meter.flags |= OFPMF13_STATS;
638
0
        } else {
639
0
            char *value;
640
641
0
            value = strtok_r(NULL, ", \t\r\n", &save_ptr);
642
0
            if (!value) {
643
0
                return xasprintf("field %s missing value", name);
644
0
            }
645
646
0
            if (!strcmp(name, "meter")) {
647
0
                if (!strcmp(value, "all")) {
648
0
                    mm->meter.meter_id = OFPM13_ALL;
649
0
                } else if (!strcmp(value, "controller")) {
650
0
                    mm->meter.meter_id = OFPM13_CONTROLLER;
651
0
                } else if (!strcmp(value, "slowpath")) {
652
0
                    mm->meter.meter_id = OFPM13_SLOWPATH;
653
0
                } else {
654
0
                    char *error = str_to_u32(value, &mm->meter.meter_id);
655
0
                    if (error) {
656
0
                        return error;
657
0
                    }
658
0
                    if (mm->meter.meter_id > OFPM13_MAX
659
0
                        || !mm->meter.meter_id) {
660
0
                        return xasprintf("invalid value for %s", name);
661
0
                    }
662
0
                }
663
0
            } else {
664
0
                return xasprintf("unknown keyword %s", name);
665
0
            }
666
0
        }
667
0
    }
668
0
    if (fields & F_METER && !mm->meter.meter_id) {
669
0
        return xstrdup("must specify 'meter'");
670
0
    }
671
0
    if (fields & F_FLAGS && !mm->meter.flags) {
672
0
        return xstrdup("meter must specify either 'kbps' or 'pktps'");
673
0
    }
674
675
0
    if (fields & F_BANDS) {
676
0
        uint16_t n_bands = 0;
677
0
        struct ofputil_meter_band *band = NULL;
678
0
        int i;
679
680
0
        for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name;
681
0
             name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
682
683
0
            char *value;
684
685
0
            value = strtok_r(NULL, ", \t\r\n", &save_ptr);
686
0
            if (!value) {
687
0
                return xasprintf("field %s missing value", name);
688
0
            }
689
690
0
            if (!strcmp(name, "type")) {
691
                /* Start a new band */
692
0
                band = ofpbuf_put_zeros(bands, sizeof *band);
693
0
                n_bands++;
694
695
0
                if (!strcmp(value, "drop")) {
696
0
                    band->type = OFPMBT13_DROP;
697
0
                } else if (!strcmp(value, "dscp_remark")) {
698
0
                    band->type = OFPMBT13_DSCP_REMARK;
699
0
                } else {
700
0
                    return xasprintf("field %s unknown value %s", name, value);
701
0
                }
702
0
            } else if (!band || !band->type) {
703
0
                return xstrdup("band must start with the 'type' keyword");
704
0
            } else if (!strcmp(name, "rate")) {
705
0
                char *error = str_to_u32(value, &band->rate);
706
0
                if (error) {
707
0
                    return error;
708
0
                }
709
0
            } else if (!strcmp(name, "burst_size")) {
710
0
                char *error = str_to_u32(value, &band->burst_size);
711
0
                if (error) {
712
0
                    return error;
713
0
                }
714
0
            } else if (!strcmp(name, "prec_level")) {
715
0
                char *error = str_to_u8(value, name, &band->prec_level);
716
0
                if (error) {
717
0
                    return error;
718
0
                }
719
0
            } else {
720
0
                return xasprintf("unknown keyword %s", name);
721
0
            }
722
0
        }
723
        /* validate bands */
724
0
        if (!n_bands) {
725
0
            return xstrdup("meter must have bands");
726
0
        }
727
728
0
        mm->meter.n_bands = n_bands;
729
0
        mm->meter.bands = ofpbuf_steal_data(bands);
730
731
0
        for (i = 0; i < n_bands; ++i) {
732
0
            band = &mm->meter.bands[i];
733
734
0
            if (!band->type) {
735
0
                return xstrdup("band must have 'type'");
736
0
            }
737
0
            if (band->type == OFPMBT13_DSCP_REMARK) {
738
0
                if (!band->prec_level) {
739
0
                    return xstrdup("'dscp_remark' band must have"
740
0
                                   " 'prec_level'");
741
0
                }
742
0
            } else {
743
0
                if (band->prec_level) {
744
0
                    return xstrdup("Only 'dscp_remark' band may have"
745
0
                                   " 'prec_level'");
746
0
                }
747
0
            }
748
0
            if (!band->rate) {
749
0
                return xstrdup("band must have 'rate'");
750
0
            }
751
0
            if (mm->meter.flags & OFPMF13_BURST) {
752
0
                if (!band->burst_size) {
753
0
                    return xstrdup("band must have 'burst_size' "
754
0
                                   "when 'burst' flag is set");
755
0
                }
756
0
            } else {
757
0
                if (band->burst_size) {
758
0
                    return xstrdup("band may have 'burst_size' only "
759
0
                                   "when 'burst' flag is set");
760
0
                }
761
0
            }
762
0
        }
763
0
    }
764
765
0
    return NULL;
766
0
}
767
768
/* Convert 'str_' (as described in the Meter Syntax section of the
769
 * ovs-ofctl man page) into 'mm' for sending the specified meter_mod
770
 * 'command' to a switch.
771
 *
772
 * Returns NULL if successful, otherwise a malloc()'d string describing the
773
 * error.  The caller is responsible for freeing the returned string.
774
 * If successful, 'mm->meter.bands' must be free()'d by the caller. */
775
char * OVS_WARN_UNUSED_RESULT
776
parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
777
                        int command, enum ofputil_protocol *usable_protocols)
778
0
{
779
0
    struct ofpbuf bands;
780
0
    char *string;
781
0
    char *error;
782
783
0
    ofpbuf_init(&bands, 64);
784
0
    string = xstrdup(str_);
785
786
0
    error = parse_ofp_meter_mod_str__(mm, string, &bands, command,
787
0
                                      usable_protocols);
788
789
0
    free(string);
790
0
    ofpbuf_uninit(&bands);
791
792
0
    return error;
793
0
}
794
795
void
796
ofputil_format_meter_mod(struct ds *s, const struct ofputil_meter_mod *mm)
797
0
{
798
0
    switch (mm->command) {
799
0
    case OFPMC13_ADD:
800
0
        ds_put_cstr(s, " ADD ");
801
0
        break;
802
0
    case OFPMC13_MODIFY:
803
0
        ds_put_cstr(s, " MOD ");
804
0
        break;
805
0
    case OFPMC13_DELETE:
806
0
        ds_put_cstr(s, " DEL ");
807
0
        break;
808
0
    default:
809
0
        ds_put_format(s, " cmd:%d ", mm->command);
810
0
    }
811
812
0
    ofputil_format_meter_config(s, &mm->meter);
813
0
}