Coverage Report

Created: 2023-03-26 07:41

/src/openvswitch/lib/ox-stat.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015, 2016 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 "ox-stat.h"
19
#include "byte-order.h"
20
#include "openvswitch/ofp-errors.h"
21
#include "openvswitch/compiler.h"
22
#include "openvswitch/ofpbuf.h"
23
#include "openvswitch/vlog.h"
24
#include "unaligned.h"
25
26
VLOG_DEFINE_THIS_MODULE(ox_stat);
27
28
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
29
30
/* OXS header
31
 * ==========
32
 *
33
 * The header is 32 bits long.  It looks like this:
34
 *
35
 * |31                              16 15            9 8 7                0
36
 * +----------------------------------+---------------+-+------------------+
37
 * |            oxs_class             |   oxs_field   |r|    oxs_length    |
38
 * +----------------------------------+---------------+-+------------------+
39
 *
40
 * where r stands for oxs_reserved.  It is followed by oxs_length bytes of
41
 * payload (the statistic's value).
42
 *
43
 * Internally, we represent a standard OXS header as a 64-bit integer with the
44
 * above information in the most-significant bits.
45
 *
46
 *
47
 * Experimenter OXS
48
 * ================
49
 *
50
 * The header is 64 bits long.  It looks like the diagram above except that a
51
 * 32-bit experimenter ID, which we call oxs_experimenter and which identifies
52
 * a vendor, is inserted just before the payload.  Experimenter OXSs are
53
 * identified by an all-1-bits oxs_class (OFPXSC_EXPERIMENTER).  The oxs_length
54
 * value *includes* the experimenter ID, so that the real payload is only
55
 * oxs_length - 4 bytes long.
56
 *
57
 * Internally, we represent an experimenter OXS header as a 64-bit integer with
58
 * the standard header in the upper 32 bits and the experimenter ID in the
59
 * lower 32 bits.  (It would be more convenient to swap the positions of the
60
 * two 32-bit words, but this would be more error-prone because experimenter
61
 * OXSs are very rarely used, so accidentally passing one through a 32-bit type
62
 * somewhere in the OVS code would be hard to find.)
63
 */
64
65
/* OXS Class IDs.
66
 * The high order bit differentiate reserved classes from member classes.
67
 * Classes 0x0000 to 0x7FFF are member classes, allocated by ONF.
68
 * Classes 0x8000 to 0xFFFE are reserved classes, reserved for standardisation.
69
 */
70
enum ofp_oxs_class {
71
    OFPXSC_OPENFLOW_BASIC = 0x8002,     /* Basic stats class for OpenFlow */
72
    OFPXSC_EXPERIMENTER = 0xFFFF,       /* Experimenter class */
73
};
74
75
/* Functions for extracting raw field values from OXS headers. */
76
0
static uint32_t oxs_experimenter(uint64_t header) { return header; }
77
0
static int oxs_class(uint64_t header) { return header >> 48; }
78
0
static int oxs_field(uint64_t header) { return (header >> 41) & 0x7f; }
79
0
static int oxs_length(uint64_t header) { return (header >> 32) & 0xff; }
80
81
static bool
82
is_experimenter_oxs(uint64_t header)
83
0
{
84
0
    return oxs_class(header) == OFPXSC_EXPERIMENTER;
85
0
}
86
87
/* The OXS header "length" field is somewhat tricky:
88
 *
89
 *     - For a standard OXS header, the length is the number of bytes of the
90
 *       payload, and the payload consists of just the value.
91
 *
92
 *     - For an experimenter OXS header, the length is the number of bytes in
93
 *       the payload plus 4 (the length of the experimenter ID).  That is, the
94
 *       experimenter ID is included in oxs_length.
95
 *
96
 * This function returns the length of the experimenter ID field in 'header'.
97
 * That is, for an experimenter OXS (when an experimenter ID is present), it
98
 * returns 4, and for a standard OXS (when no experimenter ID is present), it
99
 * returns 0. */
100
static int
101
oxs_experimenter_len(uint64_t header)
102
0
{
103
0
    return is_experimenter_oxs(header) ? 4 : 0;
104
0
}
105
106
/* Returns the number of bytes that follow the header for an OXS entry with the
107
 * given 'header'. */
108
static int
109
oxs_payload_len(uint64_t header)
110
0
{
111
0
    return oxs_length(header) - oxs_experimenter_len(header);
112
0
}
113
114
/* Returns the number of bytes in the header for an OXS entry with the given
115
 * 'header'. */
116
static int
117
oxs_header_len(uint64_t header)
118
0
{
119
0
    return 4 + oxs_experimenter_len(header);
120
0
}
121
122
/* Assembles an OXS header from its components. */
123
#define OXS_HEADER(EXPERIMENTER, CLASS, FIELD, LENGTH) \
124
0
    (((uint64_t) (CLASS) << 48) | \
125
0
     ((uint64_t) (FIELD) << 41) | \
126
0
     ((uint64_t) (LENGTH) << 32) | \
127
0
     (EXPERIMENTER))
128
129
#define OXS_HEADER_FMT "%#"PRIx32":%d:%d:%d"
130
#define OXS_HEADER_ARGS(HEADER)                                     \
131
    oxs_experimenter(HEADER), oxs_class(HEADER), oxs_field(HEADER), \
132
    oxs_length(HEADER)
133
134
/* Currently defined OXS. */
135
0
#define OXS_OF_DURATION     OXS_HEADER (0, 0x8002, OFPXST_OFB_DURATION, 8)
136
0
#define OXS_OF_IDLE_TIME    OXS_HEADER (0, 0x8002, OFPXST_OFB_IDLE_TIME, 8)
137
0
#define OXS_OF_FLOW_COUNT   OXS_HEADER (0, 0x8002, OFPXST_OFB_FLOW_COUNT, 4)
138
0
#define OXS_OF_PACKET_COUNT OXS_HEADER (0, 0x8002, OFPXST_OFB_PACKET_COUNT, 8)
139
0
#define OXS_OF_BYTE_COUNT   OXS_HEADER (0, 0x8002, OFPXST_OFB_BYTE_COUNT, 8)
140
141
/* Header for a group of OXS statistics. */
142
struct ofp_oxs_stat {
143
    ovs_be16 reserved;          /* Must be zero. */
144
    ovs_be16 length;            /* Stats Length */
145
};
146
BUILD_ASSERT_DECL(sizeof(struct ofp_oxs_stat) == 4);
147
148
static int oxs_pull_header__(struct ofpbuf *b, uint64_t *header);
149
static enum ofperr oxs_pull_raw(const uint8_t *, unsigned int stat_len,
150
                                struct oxs_stats *, uint8_t *oxs_field_set);
151
152
static int
153
oxs_pull_header__(struct ofpbuf *b, uint64_t *header)
154
0
{
155
0
    if (b->size < 4) {
156
0
        goto bad_len;
157
0
    }
158
159
0
    *header = ((uint64_t) ntohl(get_unaligned_be32(b->data))) << 32;
160
0
    if (is_experimenter_oxs(*header)) {
161
0
        if (b->size < 8) {
162
0
            goto bad_len;
163
0
        }
164
0
        *header = ntohll(get_unaligned_be64(b->data));
165
0
    }
166
0
    if (oxs_length(*header) < oxs_experimenter_len(*header)) {
167
0
        VLOG_WARN_RL(&rl, "OXS header "OXS_HEADER_FMT" has invalid length %d "
168
0
                     "(minimum is %d)",
169
0
                     OXS_HEADER_ARGS(*header), oxs_length(*header),
170
0
                     oxs_header_len(*header));
171
0
        goto error;
172
0
    }
173
0
    ofpbuf_pull(b, oxs_header_len(*header));
174
175
0
    return 0;
176
177
0
bad_len:
178
0
    VLOG_DBG_RL(&rl, "encountered partial (%"PRIu32"-byte) OXS entry",
179
0
                b->size);
180
0
error:
181
0
    *header = 0;
182
0
    return OFPERR_OFPBMC_BAD_LEN;
183
0
}
184
185
static enum ofperr
186
oxs_pull_entry__(struct ofpbuf *b, struct oxs_stats *stats,
187
                 uint8_t *oxs_field_set)
188
0
{
189
0
    uint64_t header;
190
0
    enum ofperr error = oxs_pull_header__(b, &header);
191
0
    if (error) {
192
0
        return error;
193
0
    }
194
195
0
    unsigned int payload_len = oxs_payload_len(header);
196
0
    const void *payload = ofpbuf_try_pull(b, payload_len);
197
0
    if (!payload) {
198
0
        return OFPERR_OFPBMC_BAD_LEN;
199
0
    }
200
201
0
    switch (header) {
202
0
    case OXS_OF_DURATION: {
203
0
        uint64_t duration = ntohll(get_unaligned_be64(payload));
204
0
        stats->duration_sec = duration >> 32;
205
0
        stats->duration_nsec = duration;
206
0
    }
207
0
        break;
208
0
    case OXS_OF_IDLE_TIME:
209
0
        stats->idle_age = ntohll(get_unaligned_be64(payload)) >> 32;
210
0
        break;
211
0
    case OXS_OF_PACKET_COUNT:
212
0
        stats->packet_count = ntohll(get_unaligned_be64(payload));
213
0
        break;
214
0
    case OXS_OF_BYTE_COUNT:
215
0
        stats->byte_count = ntohll(get_unaligned_be64(payload));
216
0
        break;
217
0
    case OXS_OF_FLOW_COUNT:
218
0
        stats->flow_count = ntohl(get_unaligned_be32(payload));
219
0
        break;
220
221
0
    default:
222
        /* Unknown header. */
223
0
        return 0;
224
0
    }
225
0
    if (oxs_field_set
226
0
        && oxs_class(header) == OFPXSC_OPENFLOW_BASIC
227
0
        && oxs_field(header) < CHAR_BIT * sizeof *oxs_field_set) {
228
0
        *oxs_field_set |= 1 << oxs_field(header);
229
0
    }
230
0
    return error;
231
0
}
232
233
static enum ofperr
234
oxs_pull_raw(const uint8_t * p, unsigned int stat_len,
235
             struct oxs_stats *stats, uint8_t *oxs_field_set)
236
0
{
237
0
    struct ofpbuf b = ofpbuf_const_initializer(p, stat_len);
238
0
    while (b.size) {
239
0
        const uint8_t *pos = b.data;
240
0
        enum ofperr error = oxs_pull_entry__(&b, stats, oxs_field_set);
241
0
        if (error && error != OFPERR_OFPBMC_BAD_FIELD) {
242
0
            VLOG_DBG_RL(&rl, "error parsing OXS at offset %"PRIdPTR" "
243
0
                        "within match (%s)",
244
0
                        pos - p, ofperr_to_string(error));
245
0
            return error;
246
0
        }
247
0
    }
248
0
    return 0;
249
0
}
250
251
/* Retrieve  struct ofp_oxs_stat from 'b', followed by the flow entry
252
 * statistics in OXS format.
253
 *
254
 * Returns error if message parsing fails, otherwise returns zero . */
255
enum ofperr
256
oxs_pull_stat(struct ofpbuf *b, struct oxs_stats *stats,
257
              uint16_t *statlen, uint8_t *oxs_field_set)
258
0
{
259
0
    memset(stats, 0xff, sizeof *stats);
260
261
0
    struct ofp_oxs_stat *oxs = b->data;
262
0
    if (b->size < sizeof *oxs) {
263
0
        return OFPERR_OFPBMC_BAD_LEN;
264
0
    }
265
266
0
    uint16_t stat_len = ntohs(oxs->length);
267
0
    if (stat_len < sizeof *oxs) {
268
0
        return OFPERR_OFPBMC_BAD_LEN;
269
0
    }
270
271
0
    uint8_t *p = ofpbuf_try_pull(b, ROUND_UP(stat_len, 8));
272
0
    if (!p) {
273
0
        VLOG_DBG_RL(&rl, "oxs length %u, rounded up to a "
274
0
                    "multiple of 8, is longer than space in message (max "
275
0
                    "length %" PRIu32 ")", stat_len, b->size);
276
0
        return OFPERR_OFPBMC_BAD_LEN;
277
0
    }
278
0
    *statlen = ROUND_UP(stat_len, 8);
279
0
    return oxs_pull_raw(p + sizeof *oxs, stat_len - sizeof *oxs, stats,
280
0
                        oxs_field_set);
281
0
}
282
283
static void
284
oxs_put__(struct ofpbuf *b, uint64_t header,
285
          const void *value, size_t value_size)
286
0
{
287
0
    if (is_experimenter_oxs(header)) {
288
0
        ovs_be64 be64 = htonll(header);
289
0
        ofpbuf_put(b, &be64, sizeof be64);
290
0
    } else {
291
0
        ovs_be32 be32 = htonl(header >> 32);
292
0
        ofpbuf_put(b, &be32, sizeof be32);
293
0
    }
294
295
0
    ovs_assert(oxs_payload_len(header) == value_size);
296
0
    ofpbuf_put(b, value, value_size);
297
0
}
298
299
static void
300
oxs_put32(struct ofpbuf *b, uint64_t header, uint32_t value_)
301
0
{
302
0
    ovs_be32 value = htonl(value_);
303
0
    oxs_put__(b, header, &value, sizeof value);
304
0
}
305
306
static void
307
oxs_put64(struct ofpbuf *b, uint64_t header, uint64_t value_)
308
0
{
309
0
    ovs_be64 value = htonll(value_);
310
0
    oxs_put__(b, header, &value, sizeof value);
311
0
}
312
313
/* Appends to 'b' an struct ofp_oxs_stat followed by the flow entry statistics
314
 * in OXS format , plus enough zero bytes to pad the data appended out to a
315
 * multiple of 8.
316
 *
317
 * Specify the OpenFlow version in use as 'version'.
318
 *
319
 * This function can cause 'b''s data to be reallocated.
320
 *
321
 * Returns the number of bytes appended to 'b', excluding the padding.Never
322
 * returns zero. */
323
void
324
oxs_put_stats(struct ofpbuf *b, const struct oxs_stats *stats)
325
0
{
326
0
    size_t start = b->size;
327
328
    /* Put empty header. */
329
0
    struct ofp_oxs_stat *oxs;
330
0
    ofpbuf_put_zeros(b, sizeof *oxs);
331
332
    /* Put stats. */
333
0
    if (stats->duration_sec != UINT32_MAX) {
334
0
        oxs_put64(b, OXS_OF_DURATION,
335
0
                  (((uint64_t) stats->duration_sec << 32)
336
0
                   | stats->duration_nsec));
337
0
    }
338
0
    if (stats->idle_age != UINT32_MAX) {
339
0
        oxs_put64(b, OXS_OF_IDLE_TIME, (uint64_t) stats->idle_age << 32);
340
0
    }
341
0
    if (stats->packet_count != UINT64_MAX) {
342
0
        oxs_put64(b, OXS_OF_PACKET_COUNT, stats->packet_count);
343
0
    }
344
0
    if (stats->byte_count != UINT64_MAX) {
345
0
        oxs_put64(b, OXS_OF_BYTE_COUNT, stats->byte_count);
346
0
    }
347
0
    if (stats->flow_count != UINT32_MAX) {
348
0
        oxs_put32(b, OXS_OF_FLOW_COUNT, stats->flow_count);
349
0
    }
350
351
    /* Fill in size in header, then pad to multiple of 8 bytes. */
352
0
    oxs = ofpbuf_at(b, start, sizeof *oxs);
353
0
    oxs->length = htons(b->size - start);
354
0
    ofpbuf_put_zeros(b, PAD_SIZE(b->size - start, 8));
355
0
}