Coverage Report

Created: 2025-07-01 06:51

/src/openvswitch/lib/ofp-switch.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-switch.h"
19
#include "byte-order.h"
20
#include "openvswitch/ofpbuf.h"
21
#include "openvswitch/ofp-actions.h"
22
#include "openvswitch/ofp-errors.h"
23
#include "openvswitch/ofp-msgs.h"
24
#include "openvswitch/ofp-port.h"
25
#include "openvswitch/ofp-print.h"
26
#include "util.h"
27
28
/* ofputil_switch_features */
29
30
5.11k
#define OFPC_COMMON (OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | \
31
5.11k
                     OFPC_IP_REASM | OFPC_QUEUE_STATS)
32
BUILD_ASSERT_DECL((int) OFPUTIL_C_FLOW_STATS == OFPC_FLOW_STATS);
33
BUILD_ASSERT_DECL((int) OFPUTIL_C_TABLE_STATS == OFPC_TABLE_STATS);
34
BUILD_ASSERT_DECL((int) OFPUTIL_C_PORT_STATS == OFPC_PORT_STATS);
35
BUILD_ASSERT_DECL((int) OFPUTIL_C_IP_REASM == OFPC_IP_REASM);
36
BUILD_ASSERT_DECL((int) OFPUTIL_C_QUEUE_STATS == OFPC_QUEUE_STATS);
37
BUILD_ASSERT_DECL((int) OFPUTIL_C_ARP_MATCH_IP == OFPC_ARP_MATCH_IP);
38
BUILD_ASSERT_DECL((int) OFPUTIL_C_PORT_BLOCKED == OFPC12_PORT_BLOCKED);
39
BUILD_ASSERT_DECL((int) OFPUTIL_C_BUNDLES == OFPC14_BUNDLES);
40
BUILD_ASSERT_DECL((int) OFPUTIL_C_FLOW_MONITORING == OFPC14_FLOW_MONITORING);
41
42
static uint32_t
43
ofputil_capabilities_mask(enum ofp_version ofp_version)
44
5.11k
{
45
    /* Handle capabilities whose bit is unique for all OpenFlow versions */
46
5.11k
    switch (ofp_version) {
47
2.92k
    case OFP10_VERSION:
48
3.00k
    case OFP11_VERSION:
49
3.00k
        return OFPC_COMMON | OFPC_ARP_MATCH_IP;
50
829
    case OFP12_VERSION:
51
1.12k
    case OFP13_VERSION:
52
1.12k
        return OFPC_COMMON | OFPC12_PORT_BLOCKED;
53
90
    case OFP14_VERSION:
54
991
    case OFP15_VERSION:
55
991
        return OFPC_COMMON | OFPC12_PORT_BLOCKED | OFPC14_BUNDLES
56
991
            | OFPC14_FLOW_MONITORING;
57
0
    default:
58
        /* Caller needs to check osf->header.version itself */
59
0
        return 0;
60
5.11k
    }
61
5.11k
}
62
63
/* Pulls an OpenFlow "switch_features" structure from 'b' and decodes it into
64
 * an abstract representation in '*features', readying 'b' to iterate over the
65
 * OpenFlow port structures following 'osf' with later calls to
66
 * ofputil_pull_phy_port().  Returns 0 if successful, otherwise an OFPERR_*
67
 * value.  */
68
enum ofperr
69
ofputil_pull_switch_features(struct ofpbuf *b,
70
                             struct ofputil_switch_features *features)
71
5.11k
{
72
5.11k
    const struct ofp_header *oh = b->data;
73
5.11k
    enum ofpraw raw = ofpraw_pull_assert(b);
74
5.11k
    const struct ofp_switch_features *osf = ofpbuf_pull(b, sizeof *osf);
75
5.11k
    features->datapath_id = ntohll(osf->datapath_id);
76
5.11k
    features->n_buffers = ntohl(osf->n_buffers);
77
5.11k
    features->n_tables = osf->n_tables;
78
5.11k
    features->auxiliary_id = 0;
79
80
5.11k
    features->capabilities = ntohl(osf->capabilities) &
81
5.11k
        ofputil_capabilities_mask(oh->version);
82
83
5.11k
    if (raw == OFPRAW_OFPT10_FEATURES_REPLY) {
84
2.92k
        if (osf->capabilities & htonl(OFPC10_STP)) {
85
1.16k
            features->capabilities |= OFPUTIL_C_STP;
86
1.16k
        }
87
2.92k
        features->ofpacts = ofpact_bitmap_from_openflow(osf->actions,
88
2.92k
                                                        OFP10_VERSION);
89
2.92k
    } else if (raw == OFPRAW_OFPT11_FEATURES_REPLY
90
2.19k
               || raw == OFPRAW_OFPT13_FEATURES_REPLY) {
91
2.19k
        if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) {
92
773
            features->capabilities |= OFPUTIL_C_GROUP_STATS;
93
773
        }
94
2.19k
        features->ofpacts = 0;
95
2.19k
        if (raw == OFPRAW_OFPT13_FEATURES_REPLY) {
96
1.28k
            features->auxiliary_id = osf->auxiliary_id;
97
1.28k
        }
98
2.19k
    } else {
99
0
        return OFPERR_OFPBRC_BAD_VERSION;
100
0
    }
101
102
5.11k
    return 0;
103
5.11k
}
104
105
/* In OpenFlow 1.0, 1.1, and 1.2, an OFPT_FEATURES_REPLY message lists all the
106
 * switch's ports, unless there are too many to fit.  In OpenFlow 1.3 and
107
 * later, an OFPT_FEATURES_REPLY does not list ports at all.
108
 *
109
 * Given a buffer 'b' that contains a Features Reply message, this message
110
 * checks if it contains a complete list of the switch's ports.  Returns true,
111
 * if so.  Returns false if the list is missing (OF1.3+) or incomplete
112
 * (OF1.0/1.1/1.2), and in the latter case removes all of the ports from the
113
 * message.
114
 *
115
 * When this function returns false, the caller should send an OFPST_PORT_DESC
116
 * stats request to get the ports. */
117
bool
118
ofputil_switch_features_has_ports(struct ofpbuf *b)
119
0
{
120
0
    struct ofp_header *oh = b->data;
121
0
    size_t phy_port_size;
122
123
0
    if (oh->version >= OFP13_VERSION) {
124
        /* OpenFlow 1.3+ never has ports in the feature reply. */
125
0
        return false;
126
0
    }
127
128
0
    phy_port_size = (oh->version == OFP10_VERSION
129
0
                     ? sizeof(struct ofp10_phy_port)
130
0
                     : sizeof(struct ofp11_port));
131
0
    if (ntohs(oh->length) + phy_port_size <= UINT16_MAX) {
132
        /* There's room for additional ports in the feature reply.
133
         * Assume that the list is complete. */
134
0
        return true;
135
0
    }
136
137
    /* The feature reply has no room for more ports.  Probably the list is
138
     * truncated.  Drop the ports and tell the caller to retrieve them with
139
     * OFPST_PORT_DESC. */
140
0
    b->size = sizeof *oh + sizeof(struct ofp_switch_features);
141
0
    ofpmsg_update_length(b);
142
0
    return false;
143
0
}
144
145
/* Returns a buffer owned by the caller that encodes 'features' in the format
146
 * required by 'protocol' with the given 'xid'.  The caller should append port
147
 * information to the buffer with subsequent calls to
148
 * ofputil_put_switch_features_port(). */
149
struct ofpbuf *
150
ofputil_encode_switch_features(const struct ofputil_switch_features *features,
151
                               enum ofputil_protocol protocol, ovs_be32 xid)
152
0
{
153
0
    struct ofp_switch_features *osf;
154
0
    struct ofpbuf *b;
155
0
    enum ofp_version version;
156
0
    enum ofpraw raw;
157
158
0
    version = ofputil_protocol_to_ofp_version(protocol);
159
0
    switch (version) {
160
0
    case OFP10_VERSION:
161
0
        raw = OFPRAW_OFPT10_FEATURES_REPLY;
162
0
        break;
163
0
    case OFP11_VERSION:
164
0
    case OFP12_VERSION:
165
0
        raw = OFPRAW_OFPT11_FEATURES_REPLY;
166
0
        break;
167
0
    case OFP13_VERSION:
168
0
    case OFP14_VERSION:
169
0
    case OFP15_VERSION:
170
0
        raw = OFPRAW_OFPT13_FEATURES_REPLY;
171
0
        break;
172
0
    default:
173
0
        OVS_NOT_REACHED();
174
0
    }
175
0
    b = ofpraw_alloc_xid(raw, version, xid, 0);
176
0
    osf = ofpbuf_put_zeros(b, sizeof *osf);
177
0
    osf->datapath_id = htonll(features->datapath_id);
178
0
    osf->n_buffers = htonl(features->n_buffers);
179
0
    osf->n_tables = features->n_tables;
180
181
0
    osf->capabilities = htonl(features->capabilities &
182
0
                              ofputil_capabilities_mask(version));
183
0
    switch (version) {
184
0
    case OFP10_VERSION:
185
0
        if (features->capabilities & OFPUTIL_C_STP) {
186
0
            osf->capabilities |= htonl(OFPC10_STP);
187
0
        }
188
0
        osf->actions = ofpact_bitmap_to_openflow(features->ofpacts,
189
0
                                                 OFP10_VERSION);
190
0
        break;
191
0
    case OFP13_VERSION:
192
0
    case OFP14_VERSION:
193
0
    case OFP15_VERSION:
194
0
        osf->auxiliary_id = features->auxiliary_id;
195
        /* fall through */
196
0
    case OFP11_VERSION:
197
0
    case OFP12_VERSION:
198
0
        if (features->capabilities & OFPUTIL_C_GROUP_STATS) {
199
0
            osf->capabilities |= htonl(OFPC11_GROUP_STATS);
200
0
        }
201
0
        break;
202
0
    default:
203
0
        OVS_NOT_REACHED();
204
0
    }
205
206
0
    return b;
207
0
}
208
209
/* Encodes 'pp' into the format required by the switch_features message already
210
 * in 'b', which should have been returned by ofputil_encode_switch_features(),
211
 * and appends the encoded version to 'b'. */
212
void
213
ofputil_put_switch_features_port(const struct ofputil_phy_port *pp,
214
                                 struct ofpbuf *b)
215
0
{
216
0
    const struct ofp_header *oh = b->data;
217
218
0
    if (oh->version < OFP13_VERSION) {
219
        /* Try adding a port description to the message, but drop it again if
220
         * the buffer overflows.  (This possibility for overflow is why
221
         * OpenFlow 1.3+ moved port descriptions into a multipart message.)  */
222
0
        size_t start_ofs = b->size;
223
0
        ofputil_put_phy_port(oh->version, pp, b);
224
0
        if (b->size > UINT16_MAX) {
225
0
            b->size = start_ofs;
226
0
        }
227
0
    }
228
0
}
229
230
static const char *
231
ofputil_capabilities_to_name(uint32_t bit)
232
16.5k
{
233
16.5k
    enum ofputil_capabilities capabilities = bit;
234
235
16.5k
    switch (capabilities) {
236
3.01k
    case OFPUTIL_C_FLOW_STATS:   return "FLOW_STATS";
237
1.99k
    case OFPUTIL_C_TABLE_STATS:  return "TABLE_STATS";
238
1.88k
    case OFPUTIL_C_PORT_STATS:   return "PORT_STATS";
239
2.92k
    case OFPUTIL_C_IP_REASM:     return "IP_REASM";
240
1.90k
    case OFPUTIL_C_QUEUE_STATS:  return "QUEUE_STATS";
241
930
    case OFPUTIL_C_ARP_MATCH_IP: return "ARP_MATCH_IP";
242
1.16k
    case OFPUTIL_C_STP:          return "STP";
243
773
    case OFPUTIL_C_GROUP_STATS:  return "GROUP_STATS";
244
1.03k
    case OFPUTIL_C_PORT_BLOCKED: return "PORT_BLOCKED";
245
506
    case OFPUTIL_C_BUNDLES:      return "BUNDLES";
246
437
    case OFPUTIL_C_FLOW_MONITORING: return "FLOW_MONITORING";
247
16.5k
    }
248
249
0
    return NULL;
250
16.5k
}
251
252
void
253
ofputil_switch_features_format(struct ds *s,
254
                               const struct ofputil_switch_features *features)
255
5.11k
{
256
5.11k
    ds_put_format(s, " dpid:%016"PRIx64"\n", features->datapath_id);
257
258
5.11k
    ds_put_format(s, "n_tables:%"PRIu8", n_buffers:%"PRIu32,
259
5.11k
                  features->n_tables, features->n_buffers);
260
5.11k
    if (features->auxiliary_id) {
261
1.06k
        ds_put_format(s, ", auxiliary_id:%"PRIu8, features->auxiliary_id);
262
1.06k
    }
263
5.11k
    ds_put_char(s, '\n');
264
265
5.11k
    ds_put_cstr(s, "capabilities: ");
266
5.11k
    ofp_print_bit_names(s, features->capabilities,
267
5.11k
                        ofputil_capabilities_to_name, ' ');
268
5.11k
    ds_put_char(s, '\n');
269
270
5.11k
    if (features->ofpacts) {
271
2.75k
        ds_put_cstr(s, "actions: ");
272
2.75k
        ofpact_bitmap_format(features->ofpacts, s);
273
2.75k
        ds_put_char(s, '\n');
274
2.75k
    }
275
5.11k
}
276
277
const char *
278
ofputil_frag_handling_to_string(enum ofputil_frag_handling frag)
279
8.62k
{
280
8.62k
    switch (frag) {
281
4.65k
    case OFPUTIL_FRAG_NORMAL:   return "normal";
282
815
    case OFPUTIL_FRAG_DROP:     return "drop";
283
1.79k
    case OFPUTIL_FRAG_REASM:    return "reassemble";
284
1.35k
    case OFPUTIL_FRAG_NX_MATCH: return "nx-match";
285
8.62k
    }
286
287
8.62k
    OVS_NOT_REACHED();
288
8.62k
}
289
290
bool
291
ofputil_frag_handling_from_string(const char *s,
292
                                  enum ofputil_frag_handling *frag)
293
0
{
294
0
    if (!strcasecmp(s, "normal")) {
295
0
        *frag = OFPUTIL_FRAG_NORMAL;
296
0
    } else if (!strcasecmp(s, "drop")) {
297
0
        *frag = OFPUTIL_FRAG_DROP;
298
0
    } else if (!strcasecmp(s, "reassemble")) {
299
0
        *frag = OFPUTIL_FRAG_REASM;
300
0
    } else if (!strcasecmp(s, "nx-match")) {
301
0
        *frag = OFPUTIL_FRAG_NX_MATCH;
302
0
    } else {
303
0
        return false;
304
0
    }
305
0
    return true;
306
0
}
307

308
/* ofputil_switch_config */
309
310
/* Decodes 'oh', which must be an OFPT_GET_CONFIG_REPLY or OFPT_SET_CONFIG
311
 * message, into 'config'.  Returns false if 'oh' contained any flags that
312
 * aren't specified in its version of OpenFlow, true otherwise. */
313
static bool
314
ofputil_decode_switch_config(const struct ofp_header *oh,
315
                             struct ofputil_switch_config *config)
316
10.9k
{
317
10.9k
    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
318
10.9k
    ofpraw_pull_assert(&b);
319
320
10.9k
    const struct ofp_switch_config *osc = ofpbuf_pull(&b, sizeof *osc);
321
10.9k
    config->frag = ntohs(osc->flags) & OFPC_FRAG_MASK;
322
10.9k
    config->miss_send_len = ntohs(osc->miss_send_len);
323
324
10.9k
    ovs_be16 valid_mask = htons(OFPC_FRAG_MASK);
325
10.9k
    if (oh->version < OFP13_VERSION) {
326
6.74k
        const ovs_be16 ttl_bit = htons(OFPC_INVALID_TTL_TO_CONTROLLER);
327
6.74k
        valid_mask |= ttl_bit;
328
6.74k
        config->invalid_ttl_to_controller = (osc->flags & ttl_bit) != 0;
329
6.74k
    } else {
330
4.19k
        config->invalid_ttl_to_controller = -1;
331
4.19k
    }
332
333
10.9k
    return !(osc->flags & ~valid_mask);
334
10.9k
}
335
336
void
337
ofputil_decode_get_config_reply(const struct ofp_header *oh,
338
                                struct ofputil_switch_config *config)
339
8.18k
{
340
8.18k
    ofputil_decode_switch_config(oh, config);
341
8.18k
}
342
343
enum ofperr
344
ofputil_decode_set_config(const struct ofp_header *oh,
345
                          struct ofputil_switch_config *config)
346
2.75k
{
347
2.75k
    return (ofputil_decode_switch_config(oh, config)
348
2.75k
            ? 0
349
2.75k
            : OFPERR_OFPSCFC_BAD_FLAGS);
350
2.75k
}
351
352
static struct ofpbuf *
353
ofputil_put_switch_config(const struct ofputil_switch_config *config,
354
                          struct ofpbuf *b)
355
0
{
356
0
    const struct ofp_header *oh = b->data;
357
0
    struct ofp_switch_config *osc = ofpbuf_put_zeros(b, sizeof *osc);
358
0
    osc->flags = htons(config->frag);
359
0
    if (config->invalid_ttl_to_controller > 0 && oh->version < OFP13_VERSION) {
360
0
        osc->flags |= htons(OFPC_INVALID_TTL_TO_CONTROLLER);
361
0
    }
362
0
    osc->miss_send_len = htons(config->miss_send_len);
363
0
    return b;
364
0
}
365
366
struct ofpbuf *
367
ofputil_encode_get_config_reply(const struct ofp_header *request,
368
                                const struct ofputil_switch_config *config)
369
0
{
370
0
    struct ofpbuf *b = ofpraw_alloc_reply(OFPRAW_OFPT_GET_CONFIG_REPLY,
371
0
                                          request, 0);
372
0
    return ofputil_put_switch_config(config, b);
373
0
}
374
375
struct ofpbuf *
376
ofputil_encode_set_config(const struct ofputil_switch_config *config,
377
                          enum ofp_version version)
378
0
{
379
0
    struct ofpbuf *b = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, version, 0);
380
0
    return ofputil_put_switch_config(config, b);
381
0
}
382
383
void
384
ofputil_switch_config_format(struct ds *s,
385
                             const struct ofputil_switch_config *config)
386
8.62k
{
387
8.62k
    ds_put_format(s, " frags=%s",
388
8.62k
                  ofputil_frag_handling_to_string(config->frag));
389
390
8.62k
    if (config->invalid_ttl_to_controller > 0) {
391
4.48k
        ds_put_format(s, " invalid_ttl_to_controller");
392
4.48k
    }
393
394
8.62k
    ds_put_format(s, " miss_send_len=%"PRIu16"\n", config->miss_send_len);
395
8.62k
}