Coverage Report

Created: 2025-07-11 06:11

/src/openvswitch/lib/ofp-bundle.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-bundle.h"
19
#include <errno.h>
20
#include <stdlib.h>
21
#include "openvswitch/ofp-parse.h"
22
#include "openvswitch/ofp-print.h"
23
#include "openvswitch/ofpbuf.h"
24
#include "openvswitch/vlog.h"
25
#include "util.h"
26
27
VLOG_DEFINE_THIS_MODULE(ofp_bundle);
28
29
/* Destroys 'bms'. */
30
void
31
ofputil_free_bundle_msgs(struct ofputil_bundle_msg *bms, size_t n_bms)
32
0
{
33
0
    for (size_t i = 0; i < n_bms; i++) {
34
0
        switch ((int)bms[i].type) {
35
0
        case OFPTYPE_FLOW_MOD:
36
0
            free(CONST_CAST(struct ofpact *, bms[i].fm.ofpacts));
37
0
            minimatch_destroy(&bms[i].fm.match);
38
0
            break;
39
0
        case OFPTYPE_GROUP_MOD:
40
0
            ofputil_uninit_group_mod(&bms[i].gm);
41
0
            break;
42
0
        case OFPTYPE_PACKET_OUT:
43
0
            free(bms[i].po.ofpacts);
44
0
            free(CONST_CAST(void *, bms[i].po.packet));
45
0
            break;
46
0
        default:
47
0
            break;
48
0
        }
49
0
    }
50
0
    free(bms);
51
0
}
52
53
void
54
ofputil_encode_bundle_msgs(const struct ofputil_bundle_msg *bms,
55
                           size_t n_bms, struct ovs_list *requests,
56
                           enum ofputil_protocol protocol)
57
0
{
58
0
    enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
59
60
0
    for (size_t i = 0; i < n_bms; i++) {
61
0
        struct ofpbuf *request = NULL;
62
63
0
        switch ((int)bms[i].type) {
64
0
        case OFPTYPE_FLOW_MOD:
65
0
            request = ofputil_encode_flow_mod(&bms[i].fm, protocol);
66
0
            break;
67
0
        case OFPTYPE_GROUP_MOD:
68
0
            request = ofputil_encode_group_mod(version, &bms[i].gm, NULL, -1);
69
0
            break;
70
0
        case OFPTYPE_PACKET_OUT:
71
0
            request = ofputil_encode_packet_out(&bms[i].po, protocol);
72
0
            break;
73
0
        default:
74
0
            break;
75
0
        }
76
0
        if (request) {
77
0
            ovs_list_push_back(requests, &request->list_node);
78
0
        }
79
0
    }
80
0
}
81
82
enum ofperr
83
ofputil_decode_bundle_ctrl(const struct ofp_header *oh,
84
                           struct ofputil_bundle_ctrl_msg *msg)
85
0
{
86
0
    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
87
0
    enum ofpraw raw = ofpraw_pull_assert(&b);
88
0
    ovs_assert(raw == OFPRAW_OFPT14_BUNDLE_CONTROL
89
0
               || raw == OFPRAW_ONFT13_BUNDLE_CONTROL);
90
91
0
    const struct ofp14_bundle_ctrl_msg *m = b.msg;
92
0
    msg->bundle_id = ntohl(m->bundle_id);
93
0
    msg->type = ntohs(m->type);
94
0
    msg->flags = ntohs(m->flags);
95
96
0
    return 0;
97
0
}
98
99
struct ofpbuf *
100
ofputil_encode_bundle_ctrl_request(enum ofp_version ofp_version,
101
                                   struct ofputil_bundle_ctrl_msg *bc)
102
0
{
103
0
    struct ofpbuf *request;
104
0
    struct ofp14_bundle_ctrl_msg *m;
105
106
0
    switch (ofp_version) {
107
0
    case OFP10_VERSION:
108
0
    case OFP11_VERSION:
109
0
    case OFP12_VERSION:
110
0
        ovs_fatal(0, "bundles need OpenFlow 1.3 or later "
111
0
                     "(\'-O OpenFlow14\')");
112
0
    case OFP13_VERSION:
113
0
    case OFP14_VERSION:
114
0
    case OFP15_VERSION:
115
0
        request = ofpraw_alloc(ofp_version == OFP13_VERSION
116
0
                               ? OFPRAW_ONFT13_BUNDLE_CONTROL
117
0
                               : OFPRAW_OFPT14_BUNDLE_CONTROL, ofp_version, 0);
118
0
        m = ofpbuf_put_zeros(request, sizeof *m);
119
120
0
        m->bundle_id = htonl(bc->bundle_id);
121
0
        m->type = htons(bc->type);
122
0
        m->flags = htons(bc->flags);
123
0
        break;
124
0
    default:
125
0
        OVS_NOT_REACHED();
126
0
    }
127
128
0
    return request;
129
0
}
130
131
static const char *
132
bundle_flags_to_name(uint32_t bit)
133
0
{
134
0
    switch (bit) {
135
0
    case OFPBF_ATOMIC:
136
0
        return "atomic";
137
0
    case OFPBF_ORDERED:
138
0
        return "ordered";
139
0
    default:
140
0
        return NULL;
141
0
    }
142
0
}
143
144
void
145
ofputil_format_bundle_ctrl_request(struct ds *s,
146
                                   const struct ofputil_bundle_ctrl_msg *bctrl)
147
0
{
148
0
    ds_put_char(s, '\n');
149
0
    ds_put_format(s, " bundle_id=%#"PRIx32" type=",  bctrl->bundle_id);
150
0
    switch (bctrl->type) {
151
0
    case OFPBCT_OPEN_REQUEST:
152
0
        ds_put_cstr(s, "OPEN_REQUEST");
153
0
        break;
154
0
    case OFPBCT_OPEN_REPLY:
155
0
        ds_put_cstr(s, "OPEN_REPLY");
156
0
        break;
157
0
    case OFPBCT_CLOSE_REQUEST:
158
0
        ds_put_cstr(s, "CLOSE_REQUEST");
159
0
        break;
160
0
    case OFPBCT_CLOSE_REPLY:
161
0
        ds_put_cstr(s, "CLOSE_REPLY");
162
0
        break;
163
0
    case OFPBCT_COMMIT_REQUEST:
164
0
        ds_put_cstr(s, "COMMIT_REQUEST");
165
0
        break;
166
0
    case OFPBCT_COMMIT_REPLY:
167
0
        ds_put_cstr(s, "COMMIT_REPLY");
168
0
        break;
169
0
    case OFPBCT_DISCARD_REQUEST:
170
0
        ds_put_cstr(s, "DISCARD_REQUEST");
171
0
        break;
172
0
    case OFPBCT_DISCARD_REPLY:
173
0
        ds_put_cstr(s, "DISCARD_REPLY");
174
0
        break;
175
0
    }
176
177
0
    ds_put_cstr(s, " flags=");
178
0
    ofp_print_bit_names(s, bctrl->flags, bundle_flags_to_name, ' ');
179
0
}
180
181
182
struct ofpbuf *
183
ofputil_encode_bundle_ctrl_reply(const struct ofp_header *oh,
184
                                 struct ofputil_bundle_ctrl_msg *msg)
185
0
{
186
0
    struct ofpbuf *buf;
187
0
    struct ofp14_bundle_ctrl_msg *m;
188
189
0
    buf = ofpraw_alloc_reply(oh->version == OFP13_VERSION
190
0
                             ? OFPRAW_ONFT13_BUNDLE_CONTROL
191
0
                             : OFPRAW_OFPT14_BUNDLE_CONTROL, oh, 0);
192
0
    m = ofpbuf_put_zeros(buf, sizeof *m);
193
194
0
    m->bundle_id = htonl(msg->bundle_id);
195
0
    m->type = htons(msg->type);
196
0
    m->flags = htons(msg->flags);
197
198
0
    return buf;
199
0
}
200
201
/* Return true for bundlable state change requests, false for other messages.
202
 */
203
static bool
204
ofputil_is_bundlable(enum ofptype type)
205
0
{
206
0
    switch (type) {
207
        /* Minimum required by OpenFlow 1.4. */
208
0
    case OFPTYPE_PORT_MOD:
209
0
    case OFPTYPE_FLOW_MOD:
210
        /* Other supported types. */
211
0
    case OFPTYPE_GROUP_MOD:
212
0
    case OFPTYPE_PACKET_OUT:
213
0
        return true;
214
215
        /* Nice to have later. */
216
0
    case OFPTYPE_FLOW_MOD_TABLE_ID:
217
0
    case OFPTYPE_TABLE_MOD:
218
0
    case OFPTYPE_METER_MOD:
219
0
    case OFPTYPE_NXT_TLV_TABLE_MOD:
220
221
        /* Not to be bundlable. */
222
0
    case OFPTYPE_ECHO_REQUEST:
223
0
    case OFPTYPE_FEATURES_REQUEST:
224
0
    case OFPTYPE_GET_CONFIG_REQUEST:
225
0
    case OFPTYPE_SET_CONFIG:
226
0
    case OFPTYPE_BARRIER_REQUEST:
227
0
    case OFPTYPE_ROLE_REQUEST:
228
0
    case OFPTYPE_ECHO_REPLY:
229
0
    case OFPTYPE_SET_FLOW_FORMAT:
230
0
    case OFPTYPE_SET_PACKET_IN_FORMAT:
231
0
    case OFPTYPE_SET_CONTROLLER_ID:
232
0
    case OFPTYPE_FLOW_AGE:
233
0
    case OFPTYPE_FLOW_MONITOR_CANCEL:
234
0
    case OFPTYPE_SET_ASYNC_CONFIG:
235
0
    case OFPTYPE_GET_ASYNC_REQUEST:
236
0
    case OFPTYPE_DESC_STATS_REQUEST:
237
0
    case OFPTYPE_FLOW_STATS_REQUEST:
238
0
    case OFPTYPE_AGGREGATE_STATS_REQUEST:
239
0
    case OFPTYPE_TABLE_STATS_REQUEST:
240
0
    case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
241
0
    case OFPTYPE_TABLE_DESC_REQUEST:
242
0
    case OFPTYPE_PORT_STATS_REQUEST:
243
0
    case OFPTYPE_QUEUE_STATS_REQUEST:
244
0
    case OFPTYPE_PORT_DESC_STATS_REQUEST:
245
0
    case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
246
0
    case OFPTYPE_METER_STATS_REQUEST:
247
0
    case OFPTYPE_METER_CONFIG_STATS_REQUEST:
248
0
    case OFPTYPE_METER_FEATURES_STATS_REQUEST:
249
0
    case OFPTYPE_GROUP_STATS_REQUEST:
250
0
    case OFPTYPE_GROUP_DESC_STATS_REQUEST:
251
0
    case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
252
0
    case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
253
0
    case OFPTYPE_BUNDLE_CONTROL:
254
0
    case OFPTYPE_BUNDLE_ADD_MESSAGE:
255
0
    case OFPTYPE_HELLO:
256
0
    case OFPTYPE_ERROR:
257
0
    case OFPTYPE_FEATURES_REPLY:
258
0
    case OFPTYPE_GET_CONFIG_REPLY:
259
0
    case OFPTYPE_PACKET_IN:
260
0
    case OFPTYPE_FLOW_REMOVED:
261
0
    case OFPTYPE_PORT_STATUS:
262
0
    case OFPTYPE_BARRIER_REPLY:
263
0
    case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
264
0
    case OFPTYPE_DESC_STATS_REPLY:
265
0
    case OFPTYPE_FLOW_STATS_REPLY:
266
0
    case OFPTYPE_QUEUE_STATS_REPLY:
267
0
    case OFPTYPE_PORT_STATS_REPLY:
268
0
    case OFPTYPE_TABLE_STATS_REPLY:
269
0
    case OFPTYPE_AGGREGATE_STATS_REPLY:
270
0
    case OFPTYPE_PORT_DESC_STATS_REPLY:
271
0
    case OFPTYPE_ROLE_REPLY:
272
0
    case OFPTYPE_FLOW_MONITOR_PAUSED:
273
0
    case OFPTYPE_FLOW_MONITOR_RESUMED:
274
0
    case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
275
0
    case OFPTYPE_GET_ASYNC_REPLY:
276
0
    case OFPTYPE_GROUP_STATS_REPLY:
277
0
    case OFPTYPE_GROUP_DESC_STATS_REPLY:
278
0
    case OFPTYPE_GROUP_FEATURES_STATS_REPLY:
279
0
    case OFPTYPE_METER_STATS_REPLY:
280
0
    case OFPTYPE_METER_CONFIG_STATS_REPLY:
281
0
    case OFPTYPE_METER_FEATURES_STATS_REPLY:
282
0
    case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
283
0
    case OFPTYPE_TABLE_DESC_REPLY:
284
0
    case OFPTYPE_ROLE_STATUS:
285
0
    case OFPTYPE_REQUESTFORWARD:
286
0
    case OFPTYPE_TABLE_STATUS:
287
0
    case OFPTYPE_NXT_TLV_TABLE_REQUEST:
288
0
    case OFPTYPE_NXT_TLV_TABLE_REPLY:
289
0
    case OFPTYPE_NXT_RESUME:
290
0
    case OFPTYPE_IPFIX_BRIDGE_STATS_REQUEST:
291
0
    case OFPTYPE_IPFIX_BRIDGE_STATS_REPLY:
292
0
    case OFPTYPE_IPFIX_FLOW_STATS_REQUEST:
293
0
    case OFPTYPE_IPFIX_FLOW_STATS_REPLY:
294
0
    case OFPTYPE_CT_FLUSH_ZONE:
295
0
    case OFPTYPE_CT_FLUSH:
296
0
        break;
297
0
    }
298
299
0
    return false;
300
0
}
301
302
enum ofperr
303
ofputil_decode_bundle_add(const struct ofp_header *oh,
304
                          struct ofputil_bundle_add_msg *msg,
305
                          enum ofptype *typep)
306
0
{
307
0
    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
308
309
    /* Pull the outer ofp_header. */
310
0
    enum ofpraw raw = ofpraw_pull_assert(&b);
311
0
    ovs_assert(raw == OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE
312
0
               || raw == OFPRAW_ONFT13_BUNDLE_ADD_MESSAGE);
313
314
    /* Pull the bundle_ctrl header. */
315
0
    const struct ofp14_bundle_ctrl_msg *m = ofpbuf_pull(&b, sizeof *m);
316
0
    msg->bundle_id = ntohl(m->bundle_id);
317
0
    msg->flags = ntohs(m->flags);
318
319
    /* Pull the inner ofp_header. */
320
0
    if (b.size < sizeof(struct ofp_header)) {
321
0
        return OFPERR_OFPBFC_MSG_BAD_LEN;
322
0
    }
323
0
    msg->msg = b.data;
324
0
    if (msg->msg->version != oh->version) {
325
0
        return OFPERR_OFPBFC_BAD_VERSION;
326
0
    }
327
0
    size_t inner_len = ntohs(msg->msg->length);
328
0
    if (inner_len < sizeof(struct ofp_header) || inner_len > b.size) {
329
0
        return OFPERR_OFPBFC_MSG_BAD_LEN;
330
0
    }
331
0
    if (msg->msg->xid != oh->xid) {
332
0
        return OFPERR_OFPBFC_MSG_BAD_XID;
333
0
    }
334
335
    /* Reject unbundlable messages. */
336
0
    enum ofptype type;
337
0
    enum ofperr error = ofptype_decode(&type, msg->msg);
338
0
    if (error) {
339
0
        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
340
0
        VLOG_WARN_RL(&rl, "OFPT14_BUNDLE_ADD_MESSAGE contained "
341
0
                     "message is unparsable (%s)", ofperr_get_name(error));
342
0
        return OFPERR_OFPBFC_MSG_UNSUP; /* 'error' would be confusing. */
343
0
    }
344
345
0
    if (!ofputil_is_bundlable(type)) {
346
0
        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
347
0
        VLOG_WARN_RL(&rl, "%s message not allowed inside "
348
0
                     "OFPT14_BUNDLE_ADD_MESSAGE", ofptype_get_name(type));
349
0
        return OFPERR_OFPBFC_MSG_UNSUP;
350
0
    }
351
0
    if (typep) {
352
0
        *typep = type;
353
0
    }
354
355
0
    return 0;
356
0
}
357
358
struct ofpbuf *
359
ofputil_encode_bundle_add(enum ofp_version ofp_version,
360
                          struct ofputil_bundle_add_msg *msg)
361
0
{
362
0
    struct ofpbuf *request;
363
0
    struct ofp14_bundle_ctrl_msg *m;
364
365
    /* Must use the same xid as the embedded message. */
366
0
    request = ofpraw_alloc_xid(ofp_version == OFP13_VERSION
367
0
                               ? OFPRAW_ONFT13_BUNDLE_ADD_MESSAGE
368
0
                               : OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE, ofp_version,
369
0
                               msg->msg->xid, ntohs(msg->msg->length));
370
0
    m = ofpbuf_put_zeros(request, sizeof *m);
371
372
0
    m->bundle_id = htonl(msg->bundle_id);
373
0
    m->flags = htons(msg->flags);
374
0
    ofpbuf_put(request, msg->msg, ntohs(msg->msg->length));
375
376
0
    ofpmsg_update_length(request);
377
0
    return request;
378
0
}
379
380
/* Opens file 'file_name' and reads each line as a flow_mod or a group_mod,
381
 * depending on the first keyword on each line.  Stores each flow and group
382
 * mods in '*bms', an array allocated on the caller's behalf, and the number of
383
 * messages in '*n_bms'.
384
 *
385
 * Returns NULL if successful, otherwise a malloc()'d string describing the
386
 * error.  The caller is responsible for freeing the returned string. */
387
char * OVS_WARN_UNUSED_RESULT
388
parse_ofp_bundle_file(const char *file_name,
389
                      const struct ofputil_port_map *port_map,
390
                      const struct ofputil_table_map *table_map,
391
                      struct ofputil_bundle_msg **bms, size_t *n_bms,
392
                      enum ofputil_protocol *usable_protocols)
393
0
{
394
0
    size_t allocated_bms;
395
0
    char *error = NULL;
396
0
    int line_number;
397
0
    FILE *stream;
398
0
    struct ds ds;
399
400
0
    *usable_protocols = OFPUTIL_P_ANY;
401
402
0
    *bms = NULL;
403
0
    *n_bms = 0;
404
405
0
    stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r");
406
0
    if (stream == NULL) {
407
0
        return xasprintf("%s: open failed (%s)",
408
0
                         file_name, ovs_strerror(errno));
409
0
    }
410
411
0
    allocated_bms = *n_bms;
412
0
    ds_init(&ds);
413
0
    line_number = 0;
414
0
    while (!ds_get_preprocessed_line(&ds, stream, &line_number)) {
415
0
        enum ofputil_protocol usable;
416
0
        char *s = ds_cstr(&ds);
417
0
        size_t len;
418
419
0
        if (*n_bms >= allocated_bms) {
420
0
            struct ofputil_bundle_msg *new_bms;
421
422
0
            new_bms = x2nrealloc(*bms, &allocated_bms, sizeof **bms);
423
0
            for (size_t i = 0; i < *n_bms; i++) {
424
0
                if (new_bms[i].type == OFPTYPE_GROUP_MOD) {
425
0
                    ovs_list_moved(&new_bms[i].gm.buckets,
426
0
                                   &(*bms)[i].gm.buckets);
427
0
                }
428
0
            }
429
0
            *bms = new_bms;
430
0
        }
431
432
0
        s += strspn(s, " \t\r\n");   /* Skip white space. */
433
0
        len = strcspn(s, ", \t\r\n"); /* Get length of the first token. */
434
435
0
        if (!strncmp(s, "flow", len)) {
436
0
            s += len;
437
0
            error = parse_ofp_flow_mod_str(&(*bms)[*n_bms].fm, s, port_map,
438
0
                                           table_map, -2, &usable);
439
0
            if (error) {
440
0
                break;
441
0
            }
442
0
            (*bms)[*n_bms].type = OFPTYPE_FLOW_MOD;
443
0
        } else if (!strncmp(s, "group", len)) {
444
0
            s += len;
445
0
            error = parse_ofp_group_mod_str(&(*bms)[*n_bms].gm, -2, s,
446
0
                                            port_map, table_map, &usable);
447
0
            if (error) {
448
0
                break;
449
0
            }
450
0
            (*bms)[*n_bms].type = OFPTYPE_GROUP_MOD;
451
0
        } else if (!strncmp(s, "packet-out", len)) {
452
0
            s += len;
453
0
            error = parse_ofp_packet_out_str(&(*bms)[*n_bms].po, s, port_map,
454
0
                                             table_map, &usable);
455
0
            if (error) {
456
0
                break;
457
0
            }
458
0
            (*bms)[*n_bms].type = OFPTYPE_PACKET_OUT;
459
0
        } else {
460
0
            error = xasprintf("Unsupported bundle message type: %.*s",
461
0
                              (int)len, s);
462
0
            break;
463
0
        }
464
465
0
        *usable_protocols &= usable; /* Each line can narrow the set. */
466
0
        *n_bms += 1;
467
0
    }
468
469
0
    ds_destroy(&ds);
470
0
    if (stream != stdin) {
471
0
        fclose(stream);
472
0
    }
473
474
0
    if (error) {
475
0
        char *err_msg = xasprintf("%s:%d: %s", file_name, line_number, error);
476
0
        free(error);
477
478
0
        ofputil_free_bundle_msgs(*bms, *n_bms);
479
0
        *bms = NULL;
480
0
        *n_bms = 0;
481
0
        return err_msg;
482
0
    }
483
0
    return NULL;
484
0
}
485
486
void
487
ofputil_format_bundle_add(struct ds *s,
488
                          const struct ofputil_bundle_add_msg *badd,
489
                          const struct ofputil_port_map *port_map,
490
                          const struct ofputil_table_map *table_map,
491
                          int verbosity)
492
0
{
493
0
    ds_put_char(s, '\n');
494
0
    ds_put_format(s, " bundle_id=%#"PRIx32, badd->bundle_id);
495
0
    ds_put_cstr(s, " flags=");
496
0
    ofp_print_bit_names(s, badd->flags, bundle_flags_to_name, ' ');
497
498
0
    ds_put_char(s, '\n');
499
0
    char *msg = ofp_to_string(badd->msg, ntohs(badd->msg->length), port_map,
500
0
                              table_map, verbosity);
501
0
    ds_put_and_free_cstr(s, msg);
502
0
}