Coverage Report

Created: 2025-07-18 06:07

/src/openvswitch/lib/ofp-group.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2008-2017, 2019 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-group.h"
19
#include <errno.h>
20
#include "byte-order.h"
21
#include "id-pool.h"
22
#include "nx-match.h"
23
#include "openvswitch/ofp-actions.h"
24
#include "openvswitch/dynamic-string.h"
25
#include "openvswitch/ofp-msgs.h"
26
#include "openvswitch/ofp-parse.h"
27
#include "openvswitch/ofp-port.h"
28
#include "openvswitch/ofp-print.h"
29
#include "openvswitch/ofp-prop.h"
30
#include "openvswitch/ofpbuf.h"
31
#include "openvswitch/vlog.h"
32
#include "util.h"
33
34
VLOG_DEFINE_THIS_MODULE(ofp_group);
35
36
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
37
38
/* Stores the group id represented by 's' into '*group_idp'.  's' may be an
39
 * integer or, for reserved group IDs, the standard OpenFlow name for the group
40
 * (either "ANY" or "ALL").
41
 *
42
 * Returns true if successful, false if 's' is not a valid OpenFlow group ID or
43
 * name. */
44
bool
45
ofputil_group_from_string(const char *s, uint32_t *group_idp)
46
553
{
47
553
    if (!strcasecmp(s, "any")) {
48
194
        *group_idp = OFPG_ANY;
49
359
    } else if (!strcasecmp(s, "all")) {
50
66
        *group_idp = OFPG_ALL;
51
293
    } else if (!str_to_uint(s, 10, group_idp)) {
52
39
        VLOG_WARN("%s is not a valid group ID.  (Valid group IDs are "
53
39
                  "32-bit nonnegative integers or the keywords ANY or "
54
39
                  "ALL.)", s);
55
39
        return false;
56
39
    }
57
58
514
    return true;
59
553
}
60
61
/* Appends to 's' a string representation of the OpenFlow group ID 'group_id'.
62
 * Most groups' string representation is just the number, but for special
63
 * groups, e.g. OFPG_ALL, it is the name, e.g. "ALL". */
64
void
65
ofputil_format_group(uint32_t group_id, struct ds *s)
66
945
{
67
945
    char name[MAX_GROUP_NAME_LEN + 1];
68
69
945
    ofputil_group_to_string(group_id, name, sizeof name);
70
945
    ds_put_cstr(s, name);
71
945
}
72
73
74
/* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string
75
 * representation of OpenFlow group ID 'group_id'.  Most group are represented
76
 * as just their number, but special groups, e.g. OFPG_ALL, are represented
77
 * by name, e.g. "ALL". */
78
void
79
ofputil_group_to_string(uint32_t group_id,
80
                        char namebuf[MAX_GROUP_NAME_LEN + 1], size_t bufsize)
81
945
{
82
945
    switch (group_id) {
83
83
    case OFPG_ALL:
84
83
        ovs_strlcpy(namebuf, "ALL", bufsize);
85
83
        break;
86
87
276
    case OFPG_ANY:
88
276
        ovs_strlcpy(namebuf, "ANY", bufsize);
89
276
        break;
90
91
586
    default:
92
586
        snprintf(namebuf, bufsize, "%"PRIu32, group_id);
93
586
        break;
94
945
    }
95
945
}
96

97
/* Frees all of the "struct ofputil_bucket"s in the 'buckets' list. */
98
void
99
ofputil_bucket_list_destroy(struct ovs_list *buckets)
100
51.2k
{
101
51.2k
    struct ofputil_bucket *bucket;
102
103
51.2k
    LIST_FOR_EACH_POP (bucket, list_node, buckets) {
104
6.69k
        free(bucket->ofpacts);
105
6.69k
        free(bucket);
106
6.69k
    }
107
51.2k
}
108
109
/* Clones 'bucket' and its ofpacts data */
110
static struct ofputil_bucket *
111
ofputil_bucket_clone_data(const struct ofputil_bucket *bucket)
112
0
{
113
0
    struct ofputil_bucket *new;
114
115
0
    new = xmemdup(bucket, sizeof *bucket);
116
0
    new->ofpacts = xmemdup(bucket->ofpacts, bucket->ofpacts_len);
117
118
0
    return new;
119
0
}
120
121
/* Clones each of the buckets in the list 'src' appending them
122
 * in turn to 'dest' which should be an initialised list.
123
 * An exception is that if the pointer value of a bucket in 'src'
124
 * matches 'skip' then it is not cloned or appended to 'dest'.
125
 * This allows all of 'src' or 'all of 'src' except 'skip' to
126
 * be cloned and appended to 'dest'. */
127
void
128
ofputil_bucket_clone_list(struct ovs_list *dest, const struct ovs_list *src,
129
                          const struct ofputil_bucket *skip)
130
0
{
131
0
    struct ofputil_bucket *bucket;
132
133
0
    LIST_FOR_EACH (bucket, list_node, src) {
134
0
        struct ofputil_bucket *new_bucket;
135
136
0
        if (bucket == skip) {
137
0
            continue;
138
0
        }
139
140
0
        new_bucket = ofputil_bucket_clone_data(bucket);
141
0
        ovs_list_push_back(dest, &new_bucket->list_node);
142
0
    }
143
0
}
144
145
/* Find a bucket in the list 'buckets' whose bucket id is 'bucket_id'
146
 * Returns the first bucket found or NULL if no buckets are found. */
147
struct ofputil_bucket *
148
ofputil_bucket_find(const struct ovs_list *buckets, uint32_t bucket_id)
149
0
{
150
0
    struct ofputil_bucket *bucket;
151
152
0
    if (bucket_id > OFPG15_BUCKET_MAX) {
153
0
        return NULL;
154
0
    }
155
156
0
    LIST_FOR_EACH (bucket, list_node, buckets) {
157
0
        if (bucket->bucket_id == bucket_id) {
158
0
            return bucket;
159
0
        }
160
0
    }
161
162
0
    return NULL;
163
0
}
164
165
/* Returns true if more than one bucket in the list 'buckets'
166
 * have the same bucket id. Returns false otherwise. */
167
bool
168
ofputil_bucket_check_duplicate_id(const struct ovs_list *buckets)
169
16.1k
{
170
16.1k
    struct ofputil_bucket *i, *j;
171
172
16.1k
    LIST_FOR_EACH (i, list_node, buckets) {
173
3.34k
        LIST_FOR_EACH_REVERSE (j, list_node, buckets) {
174
3.34k
            if (i == j) {
175
2.52k
                break;
176
2.52k
            }
177
818
            if (i->bucket_id == j->bucket_id) {
178
63
                return true;
179
63
            }
180
818
        }
181
2.58k
    }
182
183
16.0k
    return false;
184
16.1k
}
185
186
/* Returns the bucket at the front of the list 'buckets'.
187
 * Undefined if 'buckets is empty. */
188
struct ofputil_bucket *
189
ofputil_bucket_list_front(const struct ovs_list *buckets)
190
0
{
191
0
    static struct ofputil_bucket *bucket;
192
193
0
    ASSIGN_CONTAINER(bucket, ovs_list_front(buckets), list_node);
194
195
0
    return bucket;
196
0
}
197
198
/* Returns the bucket at the back of the list 'buckets'.
199
 * Undefined if 'buckets is empty. */
200
struct ofputil_bucket *
201
ofputil_bucket_list_back(const struct ovs_list *buckets)
202
0
{
203
0
    static struct ofputil_bucket *bucket;
204
205
0
    ASSIGN_CONTAINER(bucket, ovs_list_back(buckets), list_node);
206
207
0
    return bucket;
208
0
}
209
210
/* Returns an OpenFlow group stats request for OpenFlow version 'ofp_version',
211
 * that requests stats for group 'group_id'.  (Use OFPG_ALL to request stats
212
 * for all groups.)
213
 *
214
 * Group statistics include packet and byte counts for each group. */
215
struct ofpbuf *
216
ofputil_encode_group_stats_request(enum ofp_version ofp_version,
217
                                   uint32_t group_id)
218
0
{
219
0
    struct ofpbuf *msg = ofpraw_alloc((ofp_version == OFP10_VERSION
220
0
                                       ? OFPRAW_NXST_GROUP_REQUEST
221
0
                                       : OFPRAW_OFPST11_GROUP_REQUEST),
222
0
                                      ofp_version, 0);
223
0
    struct ofp11_group_stats_request *req = ofpbuf_put_zeros(msg, sizeof *req);
224
0
    req->group_id = htonl(group_id);
225
226
0
    return msg;
227
0
}
228
229
void
230
ofputil_uninit_group_desc(struct ofputil_group_desc *gd)
231
2.14k
{
232
2.14k
    ofputil_bucket_list_destroy(&gd->buckets);
233
2.14k
    ofputil_group_properties_destroy(&gd->props);
234
2.14k
}
235
236
/* Decodes the OpenFlow group description request in 'oh', returning the group
237
 * whose description is requested, or OFPG_ALL if stats for all groups was
238
 * requested. */
239
uint32_t
240
ofputil_decode_group_desc_request(const struct ofp_header *oh)
241
734
{
242
734
    struct ofpbuf request = ofpbuf_const_initializer(oh, ntohs(oh->length));
243
734
    enum ofpraw raw = ofpraw_pull_assert(&request);
244
734
    if (raw == OFPRAW_OFPST11_GROUP_DESC_REQUEST) {
245
73
        return OFPG_ALL;
246
661
    } else if (raw == OFPRAW_NXST_GROUP_DESC_REQUEST ||
247
661
               raw == OFPRAW_OFPST15_GROUP_DESC_REQUEST) {
248
661
        ovs_be32 *group_id = ofpbuf_pull(&request, sizeof *group_id);
249
661
        return ntohl(*group_id);
250
661
    } else {
251
0
        OVS_NOT_REACHED();
252
0
    }
253
734
}
254
255
/* Returns an OpenFlow group description request for OpenFlow version
256
 * 'ofp_version', that requests stats for group 'group_id'.  Use OFPG_ALL to
257
 * request stats for all groups (OpenFlow 1.4 and earlier always request all
258
 * groups).
259
 *
260
 * Group descriptions include the bucket and action configuration for each
261
 * group. */
262
struct ofpbuf *
263
ofputil_encode_group_desc_request(enum ofp_version ofp_version,
264
                                  uint32_t group_id)
265
0
{
266
0
    struct ofpbuf *request;
267
268
0
    switch (ofp_version) {
269
0
    case OFP11_VERSION:
270
0
    case OFP12_VERSION:
271
0
    case OFP13_VERSION:
272
0
    case OFP14_VERSION:
273
0
        request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_DESC_REQUEST,
274
0
                               ofp_version, 0);
275
0
        break;
276
0
    case OFP10_VERSION:
277
0
    case OFP15_VERSION: {
278
0
        struct ofp15_group_desc_request *req;
279
0
        request = ofpraw_alloc((ofp_version == OFP10_VERSION
280
0
                                ? OFPRAW_NXST_GROUP_DESC_REQUEST
281
0
                                : OFPRAW_OFPST15_GROUP_DESC_REQUEST),
282
0
                               ofp_version, 0);
283
0
        req = ofpbuf_put_zeros(request, sizeof *req);
284
0
        req->group_id = htonl(group_id);
285
0
        break;
286
0
    }
287
0
    default:
288
0
        OVS_NOT_REACHED();
289
0
    }
290
291
0
    return request;
292
0
}
293
294
295
enum ofperr
296
ofputil_group_desc_request_format(struct ds *string,
297
                                   const struct ofp_header *oh)
298
734
{
299
734
    uint32_t group_id = ofputil_decode_group_desc_request(oh);
300
734
    ds_put_cstr(string, " group_id=");
301
734
    ofputil_format_group(group_id, string);
302
303
734
    return 0;
304
734
}
305
306
static void
307
ofputil_group_bucket_counters_to_ofp11(const struct ofputil_group_stats *gs,
308
                                    struct ofp11_bucket_counter bucket_cnts[])
309
0
{
310
0
    int i;
311
312
0
    for (i = 0; i < gs->n_buckets; i++) {
313
0
       bucket_cnts[i].packet_count = htonll(gs->bucket_stats[i].packet_count);
314
0
       bucket_cnts[i].byte_count = htonll(gs->bucket_stats[i].byte_count);
315
0
    }
316
0
}
317
318
static void
319
ofputil_group_stats_to_ofp11(const struct ofputil_group_stats *gs,
320
                             struct ofp11_group_stats *gs11, size_t length,
321
                             struct ofp11_bucket_counter bucket_cnts[])
322
0
{
323
0
    memset(gs11, 0, sizeof *gs11);
324
0
    gs11->length = htons(length);
325
0
    gs11->group_id = htonl(gs->group_id);
326
0
    gs11->ref_count = htonl(gs->ref_count);
327
0
    gs11->packet_count = htonll(gs->packet_count);
328
0
    gs11->byte_count = htonll(gs->byte_count);
329
0
    ofputil_group_bucket_counters_to_ofp11(gs, bucket_cnts);
330
0
}
331
332
static void
333
ofputil_group_stats_to_ofp13(const struct ofputil_group_stats *gs,
334
                             struct ofp13_group_stats *gs13, size_t length,
335
                             struct ofp11_bucket_counter bucket_cnts[])
336
0
{
337
0
    ofputil_group_stats_to_ofp11(gs, &gs13->gs, length, bucket_cnts);
338
0
    gs13->duration_sec = htonl(gs->duration_sec);
339
0
    gs13->duration_nsec = htonl(gs->duration_nsec);
340
341
0
}
342
343
/* Encodes 'gs' properly for the format of the list of group statistics
344
 * replies already begun in 'replies' and appends it to the list.  'replies'
345
 * must have originally been initialized with ofpmp_init(). */
346
void
347
ofputil_append_group_stats(struct ovs_list *replies,
348
                           const struct ofputil_group_stats *gs)
349
0
{
350
0
    size_t bucket_counter_size;
351
0
    struct ofp11_bucket_counter *bucket_counters;
352
0
    size_t length;
353
354
0
    bucket_counter_size = gs->n_buckets * sizeof(struct ofp11_bucket_counter);
355
356
0
    switch (ofpmp_version(replies)) {
357
0
    case OFP11_VERSION:
358
0
    case OFP12_VERSION:{
359
0
            struct ofp11_group_stats *gs11;
360
361
0
            length = sizeof *gs11 + bucket_counter_size;
362
0
            gs11 = ofpmp_append(replies, length);
363
0
            bucket_counters = (struct ofp11_bucket_counter *)(gs11 + 1);
364
0
            ofputil_group_stats_to_ofp11(gs, gs11, length, bucket_counters);
365
0
            break;
366
0
        }
367
368
0
    case OFP10_VERSION:
369
0
    case OFP13_VERSION:
370
0
    case OFP14_VERSION:
371
0
    case OFP15_VERSION: {
372
0
            struct ofp13_group_stats *gs13;
373
374
0
            length = sizeof *gs13 + bucket_counter_size;
375
0
            gs13 = ofpmp_append(replies, length);
376
0
            bucket_counters = (struct ofp11_bucket_counter *)(gs13 + 1);
377
0
            ofputil_group_stats_to_ofp13(gs, gs13, length, bucket_counters);
378
0
            break;
379
0
        }
380
381
0
    default:
382
0
        OVS_NOT_REACHED();
383
0
    }
384
0
}
385
386
/* Returns an OpenFlow group features request for OpenFlow version
387
 * 'ofp_version'. */
388
struct ofpbuf *
389
ofputil_encode_group_features_request(enum ofp_version ofp_version)
390
0
{
391
0
    return ofpraw_alloc((ofp_version < OFP12_VERSION
392
0
                         ? OFPRAW_NXST_GROUP_FEATURES_REQUEST
393
0
                         : OFPRAW_OFPST12_GROUP_FEATURES_REQUEST),
394
0
                        ofp_version, 0);
395
0
}
396
397
/* Returns a OpenFlow message that encodes 'features' properly as a reply to
398
 * group features request 'request'. */
399
struct ofpbuf *
400
ofputil_encode_group_features_reply(
401
    const struct ofputil_group_features *features,
402
    const struct ofp_header *request)
403
0
{
404
0
    struct ofpbuf *reply = ofpraw_alloc_stats_reply(request, 0);
405
0
    struct ofp12_group_features_stats *ogf
406
0
        = ofpbuf_put_zeros(reply, sizeof *ogf);
407
0
    ogf->types = htonl(features->types);
408
0
    ogf->capabilities = htonl(features->capabilities);
409
0
    for (int i = 0; i < OFPGT12_N_TYPES; i++) {
410
0
        ogf->max_groups[i] = htonl(features->max_groups[i]);
411
0
        ogf->actions[i] = ofpact_bitmap_to_openflow(features->ofpacts[i],
412
0
                                                    request->version);
413
0
    }
414
415
0
    return reply;
416
0
}
417
418
/* Decodes group features reply 'oh' into 'features'. */
419
void
420
ofputil_decode_group_features_reply(const struct ofp_header *oh,
421
                                    struct ofputil_group_features *features)
422
736
{
423
736
    const struct ofp12_group_features_stats *ogf = ofpmsg_body(oh);
424
736
    int i;
425
426
736
    features->types = ntohl(ogf->types);
427
736
    features->capabilities = ntohl(ogf->capabilities);
428
3.68k
    for (i = 0; i < OFPGT12_N_TYPES; i++) {
429
2.94k
        features->max_groups[i] = ntohl(ogf->max_groups[i]);
430
2.94k
        features->ofpacts[i] = ofpact_bitmap_from_openflow(
431
2.94k
            ogf->actions[i], oh->version);
432
2.94k
    }
433
736
}
434
435
static const char *
436
group_type_to_string(enum ofp11_group_type type)
437
675
{
438
675
    switch (type) {
439
177
    case OFPGT11_ALL: return "all";
440
169
    case OFPGT11_SELECT: return "select";
441
155
    case OFPGT11_INDIRECT: return "indirect";
442
174
    case OFPGT11_FF: return "fast failover";
443
0
    default: OVS_NOT_REACHED();
444
675
    }
445
675
}
446
447
enum ofperr
448
ofputil_group_features_format(struct ds *string, const struct ofp_header *oh)
449
736
{
450
736
    struct ofputil_group_features features;
451
736
    int i;
452
453
736
    ofputil_decode_group_features_reply(oh, &features);
454
455
736
    ds_put_format(string, "\n Group table:\n");
456
736
    ds_put_format(string, "    Types:  0x%"PRIx32"\n", features.types);
457
736
    ds_put_format(string, "    Capabilities:  0x%"PRIx32"\n",
458
736
                  features.capabilities);
459
460
3.68k
    for (i = 0; i < OFPGT12_N_TYPES; i++) {
461
2.94k
        if (features.types & (1u << i)) {
462
675
            ds_put_format(string, "    %s group:\n", group_type_to_string(i));
463
675
            ds_put_format(string, "       max_groups=%#"PRIx32"\n",
464
675
                          features.max_groups[i]);
465
675
            ds_put_format(string, "       actions: ");
466
675
            ofpact_bitmap_format(features.ofpacts[i], string);
467
675
            ds_put_char(string, '\n');
468
675
        }
469
2.94k
    }
470
471
736
    return 0;
472
736
}
473
474
/* Parse a group status request message into a 32 bit OpenFlow 1.1
475
 * group ID and stores the latter in '*group_id'.
476
 * Returns 0 if successful, otherwise an OFPERR_* number. */
477
enum ofperr
478
ofputil_decode_group_stats_request(const struct ofp_header *request,
479
                                   uint32_t *group_id)
480
211
{
481
211
    const struct ofp11_group_stats_request *gsr11 = ofpmsg_body(request);
482
211
    *group_id = ntohl(gsr11->group_id);
483
211
    return 0;
484
211
}
485
486
/* Converts a group stats reply in 'msg' into an abstract ofputil_group_stats
487
 * in 'gs'.  Assigns freshly allocated memory to gs->bucket_stats for the
488
 * caller to eventually free.
489
 *
490
 * Multiple group stats replies can be packed into a single OpenFlow message.
491
 * Calling this function multiple times for a single 'msg' iterates through the
492
 * replies.  The caller must initially leave 'msg''s layer pointers null and
493
 * not modify them between calls.
494
 *
495
 * Returns 0 if successful, EOF if no replies were left in this 'msg',
496
 * otherwise a positive errno value. */
497
int
498
ofputil_decode_group_stats_reply(struct ofpbuf *msg,
499
                                 struct ofputil_group_stats *gs)
500
5.56k
{
501
5.56k
    struct ofp11_bucket_counter *obc;
502
5.56k
    struct ofp11_group_stats *ogs11;
503
5.56k
    enum ofpraw raw;
504
5.56k
    enum ofperr error;
505
5.56k
    size_t base_len;
506
5.56k
    size_t length;
507
5.56k
    size_t i;
508
509
5.56k
    gs->bucket_stats = NULL;
510
5.56k
    error = (msg->header ? ofpraw_decode(&raw, msg->header)
511
5.56k
             : ofpraw_pull(&raw, msg));
512
5.56k
    if (error) {
513
0
        return error;
514
0
    }
515
516
5.56k
    if (!msg->size) {
517
212
        return EOF;
518
212
    }
519
520
5.35k
    if (raw == OFPRAW_OFPST11_GROUP_REPLY) {
521
314
        base_len = sizeof *ogs11;
522
314
        ogs11 = ofpbuf_try_pull(msg, sizeof *ogs11);
523
314
        gs->duration_sec = gs->duration_nsec = UINT32_MAX;
524
5.03k
    } else if (raw == OFPRAW_NXST_GROUP_REPLY ||
525
5.03k
               raw == OFPRAW_OFPST13_GROUP_REPLY) {
526
5.03k
        struct ofp13_group_stats *ogs13;
527
528
5.03k
        base_len = sizeof *ogs13;
529
5.03k
        ogs13 = ofpbuf_try_pull(msg, sizeof *ogs13);
530
5.03k
        if (ogs13) {
531
4.77k
            ogs11 = &ogs13->gs;
532
4.77k
            gs->duration_sec = ntohl(ogs13->duration_sec);
533
4.77k
            gs->duration_nsec = ntohl(ogs13->duration_nsec);
534
4.77k
        } else {
535
263
            ogs11 = NULL;
536
263
        }
537
5.03k
    } else {
538
0
        OVS_NOT_REACHED();
539
0
    }
540
541
5.35k
    if (!ogs11) {
542
282
        VLOG_WARN_RL(&rl, "%s reply has %"PRIu32" leftover bytes at end",
543
282
                     ofpraw_get_name(raw), msg->size);
544
282
        return OFPERR_OFPBRC_BAD_LEN;
545
282
    }
546
5.07k
    length = ntohs(ogs11->length);
547
5.07k
    if (length < sizeof base_len) {
548
97
        VLOG_WARN_RL(&rl, "%s reply claims invalid length %"PRIuSIZE,
549
97
                     ofpraw_get_name(raw), length);
550
97
        return OFPERR_OFPBRC_BAD_LEN;
551
97
    }
552
553
4.97k
    gs->group_id = ntohl(ogs11->group_id);
554
4.97k
    gs->ref_count = ntohl(ogs11->ref_count);
555
4.97k
    gs->packet_count = ntohll(ogs11->packet_count);
556
4.97k
    gs->byte_count = ntohll(ogs11->byte_count);
557
558
4.97k
    gs->n_buckets = (length - base_len) / sizeof *obc;
559
4.97k
    obc = ofpbuf_try_pull(msg, gs->n_buckets * sizeof *obc);
560
4.97k
    if (!obc) {
561
1.58k
        VLOG_WARN_RL(&rl, "%s reply has %"PRIu32" leftover bytes at end",
562
1.58k
                     ofpraw_get_name(raw), msg->size);
563
1.58k
        return OFPERR_OFPBRC_BAD_LEN;
564
1.58k
    }
565
566
3.39k
    gs->bucket_stats = xmalloc(gs->n_buckets * sizeof *gs->bucket_stats);
567
60.1k
    for (i = 0; i < gs->n_buckets; i++) {
568
56.7k
        gs->bucket_stats[i].packet_count = ntohll(obc[i].packet_count);
569
56.7k
        gs->bucket_stats[i].byte_count = ntohll(obc[i].byte_count);
570
56.7k
    }
571
572
3.39k
    return 0;
573
4.97k
}
574
575
576
enum ofperr
577
ofputil_group_stats_request_format(struct ds *string,
578
                                   const struct ofp_header *oh)
579
211
{
580
211
    enum ofperr error;
581
211
    uint32_t group_id;
582
583
211
    error = ofputil_decode_group_stats_request(oh, &group_id);
584
211
    if (error) {
585
0
        return error;
586
0
    }
587
588
211
    ds_put_cstr(string, " group_id=");
589
211
    ofputil_format_group(group_id, string);
590
211
    return 0;
591
211
}
592
593
enum ofperr
594
ofputil_group_stats_format(struct ds *s, const struct ofp_header *oh)
595
2.17k
{
596
2.17k
    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
597
5.56k
    for (;;) {
598
5.56k
        struct ofputil_group_stats gs;
599
5.56k
        int retval;
600
601
5.56k
        retval = ofputil_decode_group_stats_reply(&b, &gs);
602
5.56k
        if (retval) {
603
2.17k
            if (retval != EOF) {
604
1.96k
                ds_put_cstr(s, " ***parse error***");
605
1.96k
                return retval;
606
1.96k
            }
607
212
            break;
608
2.17k
        }
609
610
3.39k
        ds_put_char(s, '\n');
611
612
3.39k
        ds_put_char(s, ' ');
613
3.39k
        ds_put_format(s, "group_id=%"PRIu32",", gs.group_id);
614
615
3.39k
        if (gs.duration_sec != UINT32_MAX) {
616
3.18k
            ds_put_cstr(s, "duration=");
617
3.18k
            ofp_print_duration(s, gs.duration_sec, gs.duration_nsec);
618
3.18k
            ds_put_char(s, ',');
619
3.18k
        }
620
3.39k
        ds_put_format(s, "ref_count=%"PRIu32",", gs.ref_count);
621
3.39k
        ds_put_format(s, "packet_count=%"PRIu64",", gs.packet_count);
622
3.39k
        ds_put_format(s, "byte_count=%"PRIu64"", gs.byte_count);
623
624
60.1k
        for (uint32_t bucket_i = 0; bucket_i < gs.n_buckets; bucket_i++) {
625
56.7k
            if (gs.bucket_stats[bucket_i].packet_count != UINT64_MAX) {
626
54.7k
                ds_put_format(s, ",bucket%"PRIu32":", bucket_i);
627
54.7k
                ds_put_format(s, "packet_count=%"PRIu64",", gs.bucket_stats[bucket_i].packet_count);
628
54.7k
                ds_put_format(s, "byte_count=%"PRIu64"", gs.bucket_stats[bucket_i].byte_count);
629
54.7k
            }
630
56.7k
        }
631
632
3.39k
        free(gs.bucket_stats);
633
3.39k
    }
634
212
    return 0;
635
2.17k
}
636
637
static char * OVS_WARN_UNUSED_RESULT
638
parse_bucket_str(struct ofputil_bucket *bucket, char *str_,
639
                 const struct ofputil_port_map *port_map,
640
                 const struct ofputil_table_map *table_map,
641
                 uint8_t group_type, enum ofputil_protocol *usable_protocols)
642
0
{
643
0
    char *pos, *key, *value;
644
0
    struct ofpbuf ofpacts;
645
0
    struct ds actions;
646
0
    char *error;
647
648
0
    bucket->weight = group_type == OFPGT11_SELECT ? 1 : 0;
649
0
    bucket->bucket_id = OFPG15_BUCKET_ALL;
650
0
    bucket->watch_port = OFPP_ANY;
651
0
    bucket->watch_group = OFPG_ANY;
652
653
0
    ds_init(&actions);
654
655
0
    pos = str_;
656
0
    error = NULL;
657
0
    while (ofputil_parse_key_value(&pos, &key, &value)) {
658
0
        if (!strcasecmp(key, "weight")) {
659
0
            error = str_to_u16(value, "weight", &bucket->weight);
660
0
        } else if (!strcasecmp(key, "watch_port")) {
661
0
            if (!ofputil_port_from_string(value, port_map, &bucket->watch_port)
662
0
                || (ofp_to_u16(bucket->watch_port) >= ofp_to_u16(OFPP_MAX)
663
0
                    && bucket->watch_port != OFPP_ANY
664
0
                    && bucket->watch_port != OFPP_CONTROLLER)) {
665
0
                error = xasprintf("%s: invalid watch_port", value);
666
0
            }
667
0
        } else if (!strcasecmp(key, "watch_group")) {
668
0
            error = str_to_u32(value, &bucket->watch_group);
669
0
            if (!error && bucket->watch_group > OFPG_MAX) {
670
0
                error = xasprintf("invalid watch_group id %"PRIu32,
671
0
                                  bucket->watch_group);
672
0
            }
673
0
        } else if (!strcasecmp(key, "bucket_id")) {
674
0
            error = str_to_u32(value, &bucket->bucket_id);
675
0
            if (!error && bucket->bucket_id > OFPG15_BUCKET_MAX) {
676
0
                error = xasprintf("invalid bucket_id id %"PRIu32,
677
0
                                  bucket->bucket_id);
678
0
            }
679
0
            *usable_protocols &= OFPUTIL_P_OF10_ANY | OFPUTIL_P_OF15_UP;
680
0
        } else if (!strcasecmp(key, "action") || !strcasecmp(key, "actions")) {
681
0
            ds_put_format(&actions, "%s,", value);
682
0
        } else {
683
0
            ds_put_format(&actions, "%s(%s),", key, value);
684
0
        }
685
686
0
        if (error) {
687
0
            ds_destroy(&actions);
688
0
            return error;
689
0
        }
690
0
    }
691
692
0
    if (!actions.length) {
693
0
        return xstrdup("bucket must specify actions");
694
0
    }
695
0
    if (group_type == OFPGT11_FF && !ofputil_bucket_has_liveness(bucket)) {
696
0
        return xstrdup("fast failover bucket requires watch_port or "
697
0
                       "watch_group");
698
0
    }
699
0
    ds_chomp(&actions, ',');
700
701
0
    ofpbuf_init(&ofpacts, 0);
702
0
    struct ofpact_parse_params pp = {
703
0
        .port_map = port_map,
704
0
        .table_map = table_map,
705
0
        .ofpacts = &ofpacts,
706
0
        .usable_protocols = usable_protocols,
707
0
    };
708
0
    error = ofpacts_parse_actions(ds_cstr(&actions), &pp);
709
0
    ds_destroy(&actions);
710
0
    if (error) {
711
0
        ofpbuf_uninit(&ofpacts);
712
0
        return error;
713
0
    }
714
0
    bucket->ofpacts = ofpacts.data;
715
0
    bucket->ofpacts_len = ofpacts.size;
716
717
0
    return NULL;
718
0
}
719
720
static char * OVS_WARN_UNUSED_RESULT
721
parse_select_group_field(char *s, const struct ofputil_port_map *port_map,
722
                         struct field_array *fa,
723
                         enum ofputil_protocol *usable_protocols)
724
0
{
725
0
    char *name, *value_str;
726
727
0
    while (ofputil_parse_key_value(&s, &name, &value_str)) {
728
0
        const struct mf_field *mf = mf_from_name(name);
729
730
0
        if (mf) {
731
0
            char *error;
732
0
            union mf_value value;
733
734
0
            if (bitmap_is_set(fa->used.bm, mf->id)) {
735
0
                return xasprintf("%s: duplicate field", name);
736
0
            }
737
738
0
            if (*value_str) {
739
0
                error = mf_parse_value(mf, value_str, port_map, &value);
740
0
                if (error) {
741
0
                    return error;
742
0
                }
743
744
                /* The mask cannot be all-zeros */
745
0
                if (!mf_is_tun_metadata(mf) &&
746
0
                    is_all_zeros(&value, mf->n_bytes)) {
747
0
                    return xasprintf("%s: values are wildcards here "
748
0
                                     "and must not be all-zeros", s);
749
0
                }
750
751
                /* The values parsed are masks for fields used
752
                 * by the selection method */
753
0
                if (!mf_is_mask_valid(mf, &value)) {
754
0
                    return xasprintf("%s: invalid mask for field %s",
755
0
                                     value_str, mf->name);
756
0
                }
757
0
            } else {
758
0
                memset(&value, 0xff, mf->n_bytes);
759
0
            }
760
761
0
            field_array_set(mf->id, &value, fa);
762
763
0
            if (is_all_ones(&value, mf->n_bytes)) {
764
0
                *usable_protocols &= mf->usable_protocols_exact;
765
0
            } else if (mf->usable_protocols_bitwise == mf->usable_protocols_cidr
766
0
                       || ip_is_cidr(value.be32)) {
767
0
                *usable_protocols &= mf->usable_protocols_cidr;
768
0
            } else {
769
0
                *usable_protocols &= mf->usable_protocols_bitwise;
770
0
            }
771
0
        } else {
772
0
            return xasprintf("%s: unknown field %s", s, name);
773
0
        }
774
0
    }
775
776
0
    return NULL;
777
0
}
778
779
static char * OVS_WARN_UNUSED_RESULT
780
parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, int command,
781
                          char *string,
782
                          const struct ofputil_port_map *port_map,
783
                          const struct ofputil_table_map *table_map,
784
                          enum ofputil_protocol *usable_protocols)
785
0
{
786
0
    enum {
787
0
        F_GROUP_TYPE            = 1 << 0,
788
0
        F_BUCKETS               = 1 << 1,
789
0
        F_COMMAND_BUCKET_ID     = 1 << 2,
790
0
        F_COMMAND_BUCKET_ID_ALL = 1 << 3,
791
0
    } fields;
792
0
    bool had_type = false;
793
0
    bool had_command_bucket_id = false;
794
0
    struct ofputil_bucket *bucket;
795
0
    char *error = NULL;
796
797
0
    *usable_protocols = OFPUTIL_P_ANY;
798
799
0
    if (command == -2) {
800
0
        size_t len;
801
802
0
        string += strspn(string, " \t\r\n");   /* Skip white space. */
803
0
        len = strcspn(string, ", \t\r\n"); /* Get length of the first token. */
804
805
0
        if (!strncmp(string, "add", len)) {
806
0
            command = OFPGC11_ADD;
807
0
        } else if (!strncmp(string, "delete", len)) {
808
0
            command = OFPGC11_DELETE;
809
0
        } else if (!strncmp(string, "modify", len)) {
810
0
            command = OFPGC11_MODIFY;
811
0
        } else if (!strncmp(string, "add_or_mod", len)) {
812
0
            command = OFPGC11_ADD_OR_MOD;
813
0
        } else if (!strncmp(string, "insert_bucket", len)) {
814
0
            command = OFPGC15_INSERT_BUCKET;
815
0
        } else if (!strncmp(string, "remove_bucket", len)) {
816
0
            command = OFPGC15_REMOVE_BUCKET;
817
0
        } else {
818
0
            len = 0;
819
0
            command = OFPGC11_ADD;
820
0
        }
821
0
        string += len;
822
0
    }
823
824
0
    switch (command) {
825
0
    case OFPGC11_ADD:
826
0
        fields = F_GROUP_TYPE | F_BUCKETS;
827
0
        break;
828
829
0
    case OFPGC11_DELETE:
830
0
        fields = 0;
831
0
        break;
832
833
0
    case OFPGC11_MODIFY:
834
0
        fields = F_GROUP_TYPE | F_BUCKETS;
835
0
        break;
836
837
0
    case OFPGC11_ADD_OR_MOD:
838
0
        fields = F_GROUP_TYPE | F_BUCKETS;
839
0
        break;
840
841
0
    case OFPGC15_INSERT_BUCKET:
842
0
        fields = F_BUCKETS | F_COMMAND_BUCKET_ID;
843
0
        *usable_protocols &= OFPUTIL_P_OF10_ANY | OFPUTIL_P_OF15_UP;
844
0
        break;
845
846
0
    case OFPGC15_REMOVE_BUCKET:
847
0
        fields = F_COMMAND_BUCKET_ID | F_COMMAND_BUCKET_ID_ALL;
848
0
        *usable_protocols &= OFPUTIL_P_OF10_ANY | OFPUTIL_P_OF15_UP;
849
0
        break;
850
851
0
    default:
852
0
        OVS_NOT_REACHED();
853
0
    }
854
855
0
    memset(gm, 0, sizeof *gm);
856
0
    gm->command = command;
857
0
    gm->group_id = OFPG_ANY;
858
0
    gm->command_bucket_id = OFPG15_BUCKET_ALL;
859
0
    ovs_list_init(&gm->buckets);
860
0
    if (command == OFPGC11_DELETE && string[0] == '\0') {
861
0
        gm->group_id = OFPG_ALL;
862
0
        return NULL;
863
0
    }
864
865
    /* Strip the buckets off the end of 'string', if there are any, saving a
866
     * pointer for later.  We want to parse the buckets last because the bucket
867
     * type influences bucket defaults. */
868
0
    char *bkt_str = strstr(string, "bucket=");
869
0
    if (bkt_str) {
870
0
        if (!(fields & F_BUCKETS)) {
871
0
            error = xstrdup("bucket is not needed");
872
0
            goto out;
873
0
        }
874
0
        *bkt_str = '\0';
875
0
    }
876
877
    /* Parse everything before the buckets. */
878
0
    char *pos = string;
879
0
    char *name, *value;
880
0
    while (ofputil_parse_key_value(&pos, &name, &value)) {
881
0
        if (!strcmp(name, "command_bucket_id")) {
882
0
            if (!(fields & F_COMMAND_BUCKET_ID)) {
883
0
                error = xstrdup("command bucket id is not needed");
884
0
                goto out;
885
0
            }
886
0
            if (!strcmp(value, "all")) {
887
0
                gm->command_bucket_id = OFPG15_BUCKET_ALL;
888
0
            } else if (!strcmp(value, "first")) {
889
0
                gm->command_bucket_id = OFPG15_BUCKET_FIRST;
890
0
            } else if (!strcmp(value, "last")) {
891
0
                gm->command_bucket_id = OFPG15_BUCKET_LAST;
892
0
            } else {
893
0
                error = str_to_u32(value, &gm->command_bucket_id);
894
0
                if (error) {
895
0
                    goto out;
896
0
                }
897
0
                if (gm->command_bucket_id > OFPG15_BUCKET_MAX
898
0
                    && (gm->command_bucket_id != OFPG15_BUCKET_FIRST
899
0
                        && gm->command_bucket_id != OFPG15_BUCKET_LAST
900
0
                        && gm->command_bucket_id != OFPG15_BUCKET_ALL)) {
901
0
                    error = xasprintf("invalid command bucket id %"PRIu32,
902
0
                                      gm->command_bucket_id);
903
0
                    goto out;
904
0
                }
905
0
            }
906
0
            if (gm->command_bucket_id == OFPG15_BUCKET_ALL
907
0
                && !(fields & F_COMMAND_BUCKET_ID_ALL)) {
908
0
                error = xstrdup("command_bucket_id=all is not permitted");
909
0
                goto out;
910
0
            }
911
0
            had_command_bucket_id = true;
912
0
        } else if (!strcmp(name, "group_id")) {
913
0
            if(!strcmp(value, "all")) {
914
0
                gm->group_id = OFPG_ALL;
915
0
            } else {
916
0
                error = str_to_u32(value, &gm->group_id);
917
0
                if (error) {
918
0
                    goto out;
919
0
                }
920
0
                if (gm->group_id != OFPG_ALL && gm->group_id > OFPG_MAX) {
921
0
                    error = xasprintf("invalid group id %"PRIu32,
922
0
                                      gm->group_id);
923
0
                    goto out;
924
0
                }
925
0
            }
926
0
        } else if (!strcmp(name, "type")){
927
0
            if (!(fields & F_GROUP_TYPE)) {
928
0
                error = xstrdup("type is not needed");
929
0
                goto out;
930
0
            }
931
0
            if (!strcmp(value, "all")) {
932
0
                gm->type = OFPGT11_ALL;
933
0
            } else if (!strcmp(value, "select")) {
934
0
                gm->type = OFPGT11_SELECT;
935
0
            } else if (!strcmp(value, "indirect")) {
936
0
                gm->type = OFPGT11_INDIRECT;
937
0
            } else if (!strcmp(value, "ff") ||
938
0
                       !strcmp(value, "fast_failover")) {
939
0
                gm->type = OFPGT11_FF;
940
0
            } else {
941
0
                error = xasprintf("invalid group type %s", value);
942
0
                goto out;
943
0
            }
944
0
            had_type = true;
945
0
        } else if (!strcmp(name, "selection_method")) {
946
0
            if (!(fields & F_GROUP_TYPE)) {
947
0
                error = xstrdup("selection method is not needed");
948
0
                goto out;
949
0
            }
950
0
            if (strlen(value) >= NTR_MAX_SELECTION_METHOD_LEN) {
951
0
                error = xasprintf("selection method is longer than %u"
952
0
                                  " bytes long",
953
0
                                  NTR_MAX_SELECTION_METHOD_LEN - 1);
954
0
                goto out;
955
0
            }
956
0
            memset(gm->props.selection_method, '\0',
957
0
                   NTR_MAX_SELECTION_METHOD_LEN);
958
0
            strcpy(gm->props.selection_method, value);
959
0
            *usable_protocols &= OFPUTIL_P_OF10_ANY | OFPUTIL_P_OF15_UP;
960
0
        } else if (!strcmp(name, "selection_method_param")) {
961
0
            if (!(fields & F_GROUP_TYPE)) {
962
0
                error = xstrdup("selection method param is not needed");
963
0
                goto out;
964
0
            }
965
0
            error = str_to_u64(value, &gm->props.selection_method_param);
966
0
            if (error) {
967
0
                goto out;
968
0
            }
969
0
            *usable_protocols &= OFPUTIL_P_OF10_ANY | OFPUTIL_P_OF15_UP;
970
0
        } else if (!strcmp(name, "fields")) {
971
0
            if (!(fields & F_GROUP_TYPE)) {
972
0
                error = xstrdup("fields are not needed");
973
0
                goto out;
974
0
            }
975
0
            error = parse_select_group_field(value, port_map,
976
0
                                             &gm->props.fields,
977
0
                                             usable_protocols);
978
0
            if (error) {
979
0
                goto out;
980
0
            }
981
0
            *usable_protocols &= OFPUTIL_P_OF10_ANY | OFPUTIL_P_OF15_UP;
982
0
        } else {
983
0
            error = xasprintf("unknown keyword %s", name);
984
0
            goto out;
985
0
        }
986
0
    }
987
0
    if (gm->group_id == OFPG_ANY) {
988
0
        error = xstrdup("must specify a group_id");
989
0
        goto out;
990
0
    }
991
0
    if (fields & F_GROUP_TYPE && !had_type) {
992
0
        error = xstrdup("must specify a type");
993
0
        goto out;
994
0
    }
995
996
    /* Exclude fields for non "hash" selection method. */
997
0
    if (strcmp(gm->props.selection_method, "hash") &&
998
0
        gm->props.fields.values_size) {
999
0
        error = xstrdup("fields may only be specified with "
1000
0
                        "\"selection_method=hash\"");
1001
0
        goto out;
1002
0
    }
1003
    /* Exclude selection_method_param if no selection_method is given. */
1004
0
    if (gm->props.selection_method[0] == 0
1005
0
        && gm->props.selection_method_param != 0) {
1006
0
        error = xstrdup("selection_method_param is only allowed with "
1007
0
                        "\"selection_method\"");
1008
0
        goto out;
1009
0
    }
1010
0
    if (fields & F_COMMAND_BUCKET_ID) {
1011
0
        if (!(fields & F_COMMAND_BUCKET_ID_ALL || had_command_bucket_id)) {
1012
0
            error = xstrdup("must specify a command bucket id");
1013
0
            goto out;
1014
0
        }
1015
0
    } else if (had_command_bucket_id) {
1016
0
        error = xstrdup("command bucket id is not needed");
1017
0
        goto out;
1018
0
    }
1019
1020
    /* Now parse the buckets, if any. */
1021
0
    while (bkt_str) {
1022
0
        char *next_bkt_str;
1023
1024
0
        bkt_str = strchr(bkt_str + 1, '=');
1025
0
        if (!bkt_str) {
1026
0
            error = xstrdup("must specify bucket content");
1027
0
            goto out;
1028
0
        }
1029
0
        bkt_str++;
1030
1031
0
        next_bkt_str = strstr(bkt_str, "bucket=");
1032
0
        if (next_bkt_str) {
1033
0
            *next_bkt_str = '\0';
1034
0
        }
1035
1036
0
        bucket = xzalloc(sizeof(struct ofputil_bucket));
1037
0
        error = parse_bucket_str(bucket, bkt_str, port_map, table_map,
1038
0
                                 gm->type, usable_protocols);
1039
0
        if (error) {
1040
0
            free(bucket);
1041
0
            goto out;
1042
0
        }
1043
0
        ovs_list_push_back(&gm->buckets, &bucket->list_node);
1044
1045
0
        if (gm->command != OFPGC15_INSERT_BUCKET
1046
0
            && gm->type != OFPGT11_SELECT && bucket->weight) {
1047
0
            error = xstrdup("Only select groups can have bucket weights.");
1048
0
            goto out;
1049
0
        }
1050
1051
0
        bkt_str = next_bkt_str;
1052
0
    }
1053
0
    if (gm->type == OFPGT11_INDIRECT && !ovs_list_is_short(&gm->buckets)) {
1054
0
        error = xstrdup("Indirect groups can have at most one bucket.");
1055
0
        goto out;
1056
0
    }
1057
1058
0
    return NULL;
1059
0
 out:
1060
0
    ofputil_uninit_group_mod(gm);
1061
0
    return error;
1062
0
}
1063
1064
/* If 'command' is given as -2, each line may start with a command name ("add",
1065
 * "modify", "add_or_mod", "delete", "insert_bucket", or "remove_bucket").  A
1066
 * missing command name is treated as "add".
1067
 */
1068
char * OVS_WARN_UNUSED_RESULT
1069
parse_ofp_group_mod_str(struct ofputil_group_mod *gm, int command,
1070
                        const char *str_,
1071
                        const struct ofputil_port_map *port_map,
1072
                        const struct ofputil_table_map *table_map,
1073
                        enum ofputil_protocol *usable_protocols)
1074
0
{
1075
0
    char *string = xstrdup(str_);
1076
0
    char *error = parse_ofp_group_mod_str__(gm, command, string, port_map,
1077
0
                                            table_map, usable_protocols);
1078
0
    free(string);
1079
0
    return error;
1080
0
}
1081
1082
/* If 'command' is given as -2, each line may start with a command name ("add",
1083
 * "modify", "add_or_mod", "delete", "insert_bucket", or "remove_bucket").  A
1084
 * missing command name is treated as "add".
1085
 */
1086
char * OVS_WARN_UNUSED_RESULT
1087
parse_ofp_group_mod_file(const char *file_name,
1088
                         const struct ofputil_port_map *port_map,
1089
                         const struct ofputil_table_map *table_map,
1090
                         int command,
1091
                         struct ofputil_group_mod **gms, size_t *n_gms,
1092
                         enum ofputil_protocol *usable_protocols)
1093
0
{
1094
0
    size_t allocated_gms;
1095
0
    int line_number;
1096
0
    FILE *stream;
1097
0
    struct ds s;
1098
1099
0
    *gms = NULL;
1100
0
    *n_gms = 0;
1101
1102
0
    stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r");
1103
0
    if (stream == NULL) {
1104
0
        return xasprintf("%s: open failed (%s)",
1105
0
                         file_name, ovs_strerror(errno));
1106
0
    }
1107
1108
0
    allocated_gms = *n_gms;
1109
0
    ds_init(&s);
1110
0
    line_number = 0;
1111
0
    *usable_protocols = OFPUTIL_P_ANY;
1112
0
    while (!ds_get_preprocessed_line(&s, stream, &line_number)) {
1113
0
        enum ofputil_protocol usable;
1114
0
        char *error;
1115
1116
0
        if (*n_gms >= allocated_gms) {
1117
0
            struct ofputil_group_mod *new_gms;
1118
0
            size_t i;
1119
1120
0
            new_gms = x2nrealloc(*gms, &allocated_gms, sizeof **gms);
1121
0
            for (i = 0; i < *n_gms; i++) {
1122
0
                ovs_list_moved(&new_gms[i].buckets, &(*gms)[i].buckets);
1123
0
            }
1124
0
            *gms = new_gms;
1125
0
        }
1126
0
        error = parse_ofp_group_mod_str(&(*gms)[*n_gms], command, ds_cstr(&s),
1127
0
                                        port_map, table_map, &usable);
1128
0
        if (error) {
1129
0
            size_t i;
1130
1131
0
            for (i = 0; i < *n_gms; i++) {
1132
0
                ofputil_uninit_group_mod(&(*gms)[i]);
1133
0
            }
1134
0
            free(*gms);
1135
0
            *gms = NULL;
1136
0
            *n_gms = 0;
1137
1138
0
            ds_destroy(&s);
1139
0
            if (stream != stdin) {
1140
0
                fclose(stream);
1141
0
            }
1142
1143
0
            char *ret = xasprintf("%s:%d: %s", file_name, line_number, error);
1144
0
            free(error);
1145
0
            return ret;
1146
0
        }
1147
0
        *usable_protocols &= usable;
1148
0
        *n_gms += 1;
1149
0
    }
1150
1151
0
    ds_destroy(&s);
1152
0
    if (stream != stdin) {
1153
0
        fclose(stream);
1154
0
    }
1155
0
    return NULL;
1156
0
}
1157

1158
static void
1159
ofputil_put_ofp11_bucket(const struct ofputil_bucket *bucket,
1160
                         struct ofpbuf *openflow, enum ofp_version ofp_version)
1161
0
{
1162
0
    struct ofp11_bucket *ob;
1163
0
    size_t start;
1164
1165
0
    start = openflow->size;
1166
0
    ofpbuf_put_zeros(openflow, sizeof *ob);
1167
0
    ofpacts_put_openflow_actions(bucket->ofpacts, bucket->ofpacts_len,
1168
0
                                openflow, ofp_version);
1169
0
    ob = ofpbuf_at_assert(openflow, start, sizeof *ob);
1170
0
    ob->len = htons(openflow->size - start);
1171
0
    ob->weight = htons(bucket->weight);
1172
0
    ob->watch_port = ofputil_port_to_ofp11(bucket->watch_port);
1173
0
    ob->watch_group = htonl(bucket->watch_group);
1174
0
}
1175
1176
static void
1177
ofputil_put_ofp15_bucket(const struct ofputil_bucket *bucket,
1178
                         uint32_t bucket_id, enum ofp11_group_type group_type,
1179
                         struct ofpbuf *openflow, enum ofp_version ofp_version)
1180
0
{
1181
0
    struct ofp15_bucket *ob;
1182
0
    size_t start, actions_start, actions_len;
1183
1184
0
    start = openflow->size;
1185
0
    ofpbuf_put_zeros(openflow, sizeof *ob);
1186
1187
0
    actions_start = openflow->size;
1188
0
    ofpacts_put_openflow_actions(bucket->ofpacts, bucket->ofpacts_len,
1189
0
                                 openflow, ofp_version);
1190
0
    actions_len = openflow->size - actions_start;
1191
1192
0
    if (group_type == OFPGT11_SELECT || bucket->weight) {
1193
0
        ofpprop_put_u16(openflow, OFPGBPT15_WEIGHT, bucket->weight);
1194
0
    }
1195
0
    if (bucket->watch_port != OFPP_ANY) {
1196
0
        ofpprop_put_be32(openflow, OFPGBPT15_WATCH_PORT,
1197
0
                         ofputil_port_to_ofp11(bucket->watch_port));
1198
0
    }
1199
0
    if (bucket->watch_group != OFPG_ANY) {
1200
0
        ofpprop_put_u32(openflow, OFPGBPT15_WATCH_GROUP, bucket->watch_group);
1201
0
    }
1202
1203
0
    ob = ofpbuf_at_assert(openflow, start, sizeof *ob);
1204
0
    ob->len = htons(openflow->size - start);
1205
0
    ob->action_array_len = htons(actions_len);
1206
0
    ob->bucket_id = htonl(bucket_id);
1207
0
}
1208
1209
static void
1210
ofputil_put_group_prop_ntr_selection_method(enum ofp_version ofp_version,
1211
                                            const struct ofputil_group_props *gp,
1212
                                            struct ofpbuf *openflow)
1213
0
{
1214
0
    struct ntr_group_prop_selection_method *prop;
1215
0
    size_t start;
1216
1217
0
    start = openflow->size;
1218
0
    ofpbuf_put_zeros(openflow, sizeof *prop);
1219
0
    oxm_put_field_array(openflow, &gp->fields, ofp_version);
1220
0
    prop = ofpbuf_at_assert(openflow, start, sizeof *prop);
1221
0
    prop->type = htons(OFPGPT15_EXPERIMENTER);
1222
0
    prop->experimenter = htonl(NTR_VENDOR_ID);
1223
0
    prop->exp_type = htonl(NTRT_SELECTION_METHOD);
1224
0
    strcpy(prop->selection_method, gp->selection_method);
1225
0
    prop->selection_method_param = htonll(gp->selection_method_param);
1226
0
    ofpprop_end(openflow, start);
1227
0
}
1228
1229
static void
1230
ofputil_append_ofp11_group_desc_reply(const struct ofputil_group_desc *gds,
1231
                                      const struct ovs_list *buckets,
1232
                                      struct ovs_list *replies,
1233
                                      enum ofp_version version)
1234
0
{
1235
0
    struct ofpbuf *reply = ofpbuf_from_list(ovs_list_back(replies));
1236
0
    struct ofp11_group_desc_stats *ogds;
1237
0
    struct ofputil_bucket *bucket;
1238
0
    size_t start_ogds;
1239
1240
0
    start_ogds = reply->size;
1241
0
    ofpbuf_put_zeros(reply, sizeof *ogds);
1242
0
    LIST_FOR_EACH (bucket, list_node, buckets) {
1243
0
        ofputil_put_ofp11_bucket(bucket, reply, version);
1244
0
    }
1245
0
    ogds = ofpbuf_at_assert(reply, start_ogds, sizeof *ogds);
1246
0
    ogds->length = htons(reply->size - start_ogds);
1247
0
    ogds->type = gds->type;
1248
0
    ogds->group_id = htonl(gds->group_id);
1249
1250
0
    ofpmp_postappend(replies, start_ogds);
1251
0
}
1252
1253
static void
1254
ofputil_append_ofp15_group_desc_reply(const struct ofputil_group_desc *gds,
1255
                                      const struct ovs_list *buckets,
1256
                                      struct ovs_list *replies,
1257
                                      enum ofp_version version)
1258
0
{
1259
0
    struct ofpbuf *reply = ofpbuf_from_list(ovs_list_back(replies));
1260
0
    struct ofp15_group_desc_stats *ogds;
1261
0
    struct ofputil_bucket *bucket;
1262
0
    size_t start_ogds, start_buckets;
1263
1264
0
    start_ogds = reply->size;
1265
0
    ofpbuf_put_zeros(reply, sizeof *ogds);
1266
0
    start_buckets = reply->size;
1267
0
    LIST_FOR_EACH (bucket, list_node, buckets) {
1268
0
        ofputil_put_ofp15_bucket(bucket, bucket->bucket_id,
1269
0
                                 gds->type, reply, version);
1270
0
    }
1271
0
    ogds = ofpbuf_at_assert(reply, start_ogds, sizeof *ogds);
1272
0
    ogds->type = gds->type;
1273
0
    ogds->group_id = htonl(gds->group_id);
1274
0
    ogds->bucket_list_len =  htons(reply->size - start_buckets);
1275
1276
    /* Add group properties */
1277
0
    if (gds->props.selection_method[0]) {
1278
0
        ofputil_put_group_prop_ntr_selection_method(version, &gds->props,
1279
0
                                                    reply);
1280
0
    }
1281
0
    ogds = ofpbuf_at_assert(reply, start_ogds, sizeof *ogds);
1282
0
    ogds->length = htons(reply->size - start_ogds);
1283
1284
0
    ofpmp_postappend(replies, start_ogds);
1285
0
}
1286
1287
/* Appends a group stats reply that contains the data in 'gds' to those already
1288
 * present in the list of ofpbufs in 'replies'.  'replies' should have been
1289
 * initialized with ofpmp_init(). */
1290
void
1291
ofputil_append_group_desc_reply(const struct ofputil_group_desc *gds,
1292
                                const struct ovs_list *buckets,
1293
                                struct ovs_list *replies)
1294
0
{
1295
0
    enum ofp_version version = ofpmp_version(replies);
1296
1297
0
    switch (version)
1298
0
    {
1299
0
    case OFP11_VERSION:
1300
0
    case OFP12_VERSION:
1301
0
    case OFP13_VERSION:
1302
0
    case OFP14_VERSION:
1303
0
        ofputil_append_ofp11_group_desc_reply(gds, buckets, replies, version);
1304
0
        break;
1305
1306
0
    case OFP10_VERSION:
1307
0
    case OFP15_VERSION:
1308
0
        ofputil_append_ofp15_group_desc_reply(gds, buckets, replies, version);
1309
0
        break;
1310
1311
0
    default:
1312
0
        OVS_NOT_REACHED();
1313
0
    }
1314
0
}
1315
1316
static enum ofperr
1317
ofputil_pull_ofp11_buckets(struct ofpbuf *msg, size_t buckets_length,
1318
                           enum ofp_version version, struct ovs_list *buckets)
1319
15.3k
{
1320
15.3k
    struct ofp11_bucket *ob;
1321
15.3k
    uint32_t bucket_id = 0;
1322
1323
15.3k
    ovs_list_init(buckets);
1324
19.0k
    while (buckets_length > 0) {
1325
12.1k
        struct ofputil_bucket *bucket;
1326
12.1k
        struct ofpbuf ofpacts;
1327
12.1k
        enum ofperr error;
1328
12.1k
        size_t ob_len;
1329
1330
12.1k
        ob = (buckets_length >= sizeof *ob
1331
12.1k
              ? ofpbuf_try_pull(msg, sizeof *ob)
1332
12.1k
              : NULL);
1333
12.1k
        if (!ob) {
1334
139
            VLOG_WARN_RL(&rl, "buckets end with %"PRIuSIZE" leftover bytes",
1335
139
                         buckets_length);
1336
139
            ofputil_bucket_list_destroy(buckets);
1337
139
            return OFPERR_OFPGMFC_BAD_BUCKET;
1338
139
        }
1339
1340
12.0k
        ob_len = ntohs(ob->len);
1341
12.0k
        if (ob_len < sizeof *ob) {
1342
3.41k
            VLOG_WARN_RL(&rl, "OpenFlow message bucket length "
1343
3.41k
                         "%"PRIuSIZE" is not valid", ob_len);
1344
3.41k
            ofputil_bucket_list_destroy(buckets);
1345
3.41k
            return OFPERR_OFPGMFC_BAD_BUCKET;
1346
8.63k
        } else if (ob_len > buckets_length) {
1347
1.10k
            VLOG_WARN_RL(&rl, "OpenFlow message bucket length %"PRIuSIZE" "
1348
1.10k
                         "exceeds remaining buckets data size %"PRIuSIZE,
1349
1.10k
                         ob_len, buckets_length);
1350
1.10k
            ofputil_bucket_list_destroy(buckets);
1351
1.10k
            return OFPERR_OFPGMFC_BAD_BUCKET;
1352
1.10k
        }
1353
7.52k
        buckets_length -= ob_len;
1354
1355
7.52k
        ofpbuf_init(&ofpacts, 0);
1356
7.52k
        error = ofpacts_pull_openflow_actions(msg, ob_len - sizeof *ob,
1357
7.52k
                                              version, NULL, NULL, &ofpacts);
1358
7.52k
        if (error) {
1359
3.79k
            ofpbuf_uninit(&ofpacts);
1360
3.79k
            ofputil_bucket_list_destroy(buckets);
1361
3.79k
            return error;
1362
3.79k
        }
1363
1364
3.73k
        bucket = xzalloc(sizeof *bucket);
1365
3.73k
        bucket->weight = ntohs(ob->weight);
1366
3.73k
        error = ofputil_port_from_ofp11(ob->watch_port, &bucket->watch_port);
1367
3.73k
        if (error) {
1368
126
            ofpbuf_uninit(&ofpacts);
1369
126
            ofputil_bucket_list_destroy(buckets);
1370
126
            free(bucket);
1371
126
            return OFPERR_OFPGMFC_BAD_WATCH;
1372
126
        }
1373
3.60k
        bucket->watch_group = ntohl(ob->watch_group);
1374
3.60k
        bucket->bucket_id = bucket_id++;
1375
1376
3.60k
        bucket->ofpacts = ofpbuf_steal_data(&ofpacts);
1377
3.60k
        bucket->ofpacts_len = ofpacts.size;
1378
3.60k
        ovs_list_push_back(buckets, &bucket->list_node);
1379
3.60k
    }
1380
1381
6.81k
    return 0;
1382
15.3k
}
1383
1384
static enum ofperr
1385
ofputil_pull_ofp15_buckets(struct ofpbuf *msg, size_t buckets_length,
1386
                           enum ofp_version version, uint8_t group_type,
1387
                           struct ovs_list *buckets)
1388
35.8k
{
1389
35.8k
    ovs_list_init(buckets);
1390
38.9k
    while (buckets_length > 0) {
1391
22.8k
        struct ofputil_bucket *bucket = NULL;
1392
22.8k
        struct ofpbuf ofpacts;
1393
22.8k
        enum ofperr err = OFPERR_OFPGMFC_BAD_BUCKET;
1394
22.8k
        size_t ob_len, actions_len, properties_len;
1395
22.8k
        ovs_be32 watch_port = ofputil_port_to_ofp11(OFPP_ANY);
1396
22.8k
        ovs_be32 watch_group = htonl(OFPG_ANY);
1397
22.8k
        ovs_be16 weight = htons(group_type == OFPGT11_SELECT ? 1 : 0);
1398
1399
22.8k
        ofpbuf_init(&ofpacts, 0);
1400
1401
22.8k
        struct ofp15_bucket *ob = ofpbuf_try_pull(msg, sizeof *ob);
1402
22.8k
        if (!ob) {
1403
0
            VLOG_WARN_RL(&rl, "buckets end with %"PRIuSIZE
1404
0
                         " leftover bytes", buckets_length);
1405
0
            goto err;
1406
0
        }
1407
1408
22.8k
        ob_len = ntohs(ob->len);
1409
22.8k
        actions_len = ntohs(ob->action_array_len);
1410
1411
22.8k
        if (ob_len < sizeof *ob) {
1412
697
            VLOG_WARN_RL(&rl, "OpenFlow message bucket length "
1413
697
                         "%"PRIuSIZE" is not valid", ob_len);
1414
697
            goto err;
1415
22.1k
        } else if (ob_len > buckets_length) {
1416
5.13k
            VLOG_WARN_RL(&rl, "OpenFlow message bucket length "
1417
5.13k
                         "%"PRIuSIZE" exceeds remaining buckets data size %"
1418
5.13k
                         PRIuSIZE, ob_len, buckets_length);
1419
5.13k
            goto err;
1420
16.9k
        } else if (actions_len > ob_len - sizeof *ob) {
1421
2.14k
            VLOG_WARN_RL(&rl, "OpenFlow message bucket actions "
1422
2.14k
                         "length %"PRIuSIZE" exceeds remaining bucket "
1423
2.14k
                         "data size %"PRIuSIZE, actions_len,
1424
2.14k
                         ob_len - sizeof *ob);
1425
2.14k
            goto err;
1426
2.14k
        }
1427
14.8k
        buckets_length -= ob_len;
1428
1429
14.8k
        err = ofpacts_pull_openflow_actions(msg, actions_len, version,
1430
14.8k
                                            NULL, NULL, &ofpacts);
1431
14.8k
        if (err) {
1432
1.02k
            goto err;
1433
1.02k
        }
1434
1435
13.8k
        properties_len = ob_len - sizeof *ob - actions_len;
1436
13.8k
        struct ofpbuf properties = ofpbuf_const_initializer(
1437
13.8k
            ofpbuf_pull(msg, properties_len), properties_len);
1438
32.5k
        while (properties.size > 0) {
1439
25.1k
            struct ofpbuf payload;
1440
25.1k
            uint64_t type;
1441
1442
25.1k
            err = ofpprop_pull(&properties, &payload, &type);
1443
25.1k
            if (err) {
1444
5.62k
                goto err;
1445
5.62k
            }
1446
1447
19.5k
            switch (type) {
1448
10.1k
            case OFPGBPT15_WEIGHT:
1449
10.1k
                err = ofpprop_parse_be16(&payload, &weight);
1450
10.1k
                break;
1451
1452
8.59k
            case OFPGBPT15_WATCH_PORT:
1453
8.59k
                err = ofpprop_parse_be32(&payload, &watch_port);
1454
8.59k
                break;
1455
1456
469
            case OFPGBPT15_WATCH_GROUP:
1457
469
                err = ofpprop_parse_be32(&payload, &watch_group);
1458
469
                break;
1459
1460
258
            default:
1461
258
                err = OFPPROP_UNKNOWN(false, "group bucket", type);
1462
258
                break;
1463
19.5k
            }
1464
1465
19.5k
            if (err) {
1466
782
                goto err;
1467
782
            }
1468
19.5k
        }
1469
1470
7.40k
        bucket = xzalloc(sizeof *bucket);
1471
1472
7.40k
        bucket->weight = ntohs(weight);
1473
7.40k
        err = ofputil_port_from_ofp11(watch_port, &bucket->watch_port);
1474
7.40k
        if (err) {
1475
2.92k
            err = OFPERR_OFPGMFC_BAD_WATCH;
1476
2.92k
            goto err;
1477
2.92k
        }
1478
4.48k
        bucket->watch_group = ntohl(watch_group);
1479
4.48k
        bucket->bucket_id = ntohl(ob->bucket_id);
1480
4.48k
        if (bucket->bucket_id > OFPG15_BUCKET_MAX) {
1481
1.39k
            VLOG_WARN_RL(&rl, "bucket id (%u) is out of range",
1482
1.39k
                         bucket->bucket_id);
1483
1.39k
            err = OFPERR_OFPGMFC_BAD_BUCKET;
1484
1.39k
            goto err;
1485
1.39k
        }
1486
1487
3.08k
        bucket->ofpacts = ofpbuf_steal_data(&ofpacts);
1488
3.08k
        bucket->ofpacts_len = ofpacts.size;
1489
3.08k
        ovs_list_push_back(buckets, &bucket->list_node);
1490
1491
3.08k
        continue;
1492
1493
19.7k
    err:
1494
19.7k
        free(bucket);
1495
19.7k
        ofpbuf_uninit(&ofpacts);
1496
19.7k
        ofputil_bucket_list_destroy(buckets);
1497
19.7k
        return err;
1498
4.48k
    }
1499
1500
16.1k
    if (ofputil_bucket_check_duplicate_id(buckets)) {
1501
63
        VLOG_WARN_RL(&rl, "Duplicate bucket id");
1502
63
        ofputil_bucket_list_destroy(buckets);
1503
63
        return OFPERR_OFPGMFC_BAD_BUCKET;
1504
63
    }
1505
1506
16.0k
    return 0;
1507
16.1k
}
1508
1509
static void
1510
ofputil_init_group_properties(struct ofputil_group_props *gp)
1511
58.1k
{
1512
58.1k
    memset(gp, 0, sizeof *gp);
1513
58.1k
}
1514
1515
void
1516
ofputil_group_properties_copy(struct ofputil_group_props *to,
1517
                              const struct ofputil_group_props *from)
1518
0
{
1519
0
    *to = *from;
1520
0
    to->fields.values = xmemdup(from->fields.values, from->fields.values_size);
1521
0
}
1522
1523
void
1524
ofputil_group_properties_destroy(struct ofputil_group_props *gp)
1525
22.8k
{
1526
22.8k
    free(gp->fields.values);
1527
22.8k
}
1528
1529
void
1530
ofputil_group_properties_format(const struct ofputil_group_props *gp,
1531
                                struct ds *ds)
1532
7.99k
{
1533
7.99k
    if (!gp->selection_method[0]) {
1534
4.73k
        return;
1535
4.73k
    }
1536
1537
3.26k
    ds_put_format(ds, ",selection_method=%s", gp->selection_method);
1538
3.26k
    if (gp->selection_method_param) {
1539
3.25k
        ds_put_format(ds, ",selection_method_param=%"PRIu64,
1540
3.25k
                      gp->selection_method_param);
1541
3.25k
    }
1542
1543
3.26k
    size_t n = bitmap_count1(gp->fields.used.bm, MFF_N_IDS);
1544
3.26k
    if (n == 1) {
1545
2.03k
        ds_put_cstr(ds, ",fields=");
1546
2.03k
        oxm_format_field_array(ds, &gp->fields);
1547
2.03k
    } else if (n > 1) {
1548
1.18k
        ds_put_cstr(ds, ",fields(");
1549
1.18k
        oxm_format_field_array(ds, &gp->fields);
1550
1.18k
        ds_put_char(ds, ')');
1551
1.18k
    }
1552
3.26k
}
1553
1554
static enum ofperr
1555
parse_group_prop_ntr_selection_method(struct ofpbuf *payload,
1556
                                      enum ofp11_group_type group_type,
1557
                                      enum ofp15_group_mod_command group_cmd,
1558
                                      struct ofputil_group_props *gp)
1559
9.95k
{
1560
9.95k
    struct ntr_group_prop_selection_method *prop = payload->data;
1561
9.95k
    size_t fields_len, method_len;
1562
9.95k
    enum ofperr error;
1563
1564
9.95k
    switch (group_type) {
1565
9.53k
    case OFPGT11_SELECT:
1566
9.53k
        break;
1567
43
    case OFPGT11_ALL:
1568
62
    case OFPGT11_INDIRECT:
1569
68
    case OFPGT11_FF:
1570
68
        OFPPROP_LOG(&rl, false, "ntr selection method property is "
1571
68
                    "only allowed for select groups");
1572
68
        return OFPERR_OFPBPC_BAD_VALUE;
1573
352
    default:
1574
352
        return OFPERR_OFPGMFC_BAD_TYPE;
1575
9.95k
    }
1576
1577
9.53k
    switch (group_cmd) {
1578
8.33k
    case OFPGC15_ADD:
1579
9.14k
    case OFPGC15_MODIFY:
1580
9.20k
    case OFPGC15_ADD_OR_MOD:
1581
9.20k
        break;
1582
19
    case OFPGC15_DELETE:
1583
20
    case OFPGC15_INSERT_BUCKET:
1584
271
    case OFPGC15_REMOVE_BUCKET:
1585
271
        OFPPROP_LOG(&rl, false, "ntr selection method property is "
1586
271
                    "only allowed for add and delete group modifications");
1587
271
        return OFPERR_OFPBPC_BAD_VALUE;
1588
55
    default:
1589
55
        return OFPERR_OFPGMFC_BAD_COMMAND;
1590
9.53k
    }
1591
1592
9.20k
    if (payload->size < sizeof *prop) {
1593
16
        OFPPROP_LOG(&rl, false, "ntr selection method property "
1594
16
                    "length %u is not valid", payload->size);
1595
16
        return OFPERR_OFPBPC_BAD_LEN;
1596
16
    }
1597
1598
9.19k
    method_len = strnlen(prop->selection_method, NTR_MAX_SELECTION_METHOD_LEN);
1599
1600
9.19k
    if (method_len == NTR_MAX_SELECTION_METHOD_LEN) {
1601
28
        OFPPROP_LOG(&rl, false,
1602
28
                    "ntr selection method is not null terminated");
1603
28
        return OFPERR_OFPBPC_BAD_VALUE;
1604
28
    }
1605
1606
9.16k
    if (strcmp("hash", prop->selection_method)
1607
9.16k
        && strcmp("dp_hash", prop->selection_method)) {
1608
3.19k
        OFPPROP_LOG(&rl, false,
1609
3.19k
                    "ntr selection method '%s' is not supported",
1610
3.19k
                    prop->selection_method);
1611
3.19k
        return OFPERR_OFPBPC_BAD_VALUE;
1612
3.19k
    }
1613
    /* 'method_len' is now non-zero. */
1614
1615
5.96k
    strcpy(gp->selection_method, prop->selection_method);
1616
5.96k
    gp->selection_method_param = ntohll(prop->selection_method_param);
1617
1618
5.96k
    ofpbuf_pull(payload, sizeof *prop);
1619
1620
5.96k
    fields_len = ntohs(prop->length) - sizeof *prop;
1621
5.96k
    if (fields_len && strcmp("hash", gp->selection_method)) {
1622
10
        OFPPROP_LOG(&rl, false, "ntr selection method %s "
1623
10
                    "does not support fields", gp->selection_method);
1624
10
        return OFPERR_OFPBPC_BAD_VALUE;
1625
10
    }
1626
1627
5.95k
    if (fields_len > 0) {
1628
5.90k
        error = oxm_pull_field_array(payload->data, fields_len,
1629
5.90k
                &gp->fields);
1630
5.90k
        if (error) {
1631
2.52k
            OFPPROP_LOG(&rl, false,
1632
2.52k
                    "ntr selection method fields are invalid");
1633
2.52k
            return error;
1634
2.52k
        }
1635
5.90k
    } else {
1636
        /* Selection_method "hash: w/o fields means default hash method. */
1637
50
        gp->fields.values_size = 0;
1638
50
    }
1639
1640
3.42k
    return 0;
1641
5.95k
}
1642
1643
static enum ofperr
1644
parse_ofp15_group_properties(struct ofpbuf *msg,
1645
                             enum ofp11_group_type group_type,
1646
                             enum ofp15_group_mod_command group_cmd,
1647
                             struct ofputil_group_props *gp,
1648
                             size_t properties_len)
1649
16.0k
{
1650
16.0k
    struct ofpbuf properties = ofpbuf_const_initializer(
1651
16.0k
        ofpbuf_pull(msg, properties_len), properties_len);
1652
19.4k
    while (properties.size > 0) {
1653
14.0k
        struct ofpbuf payload;
1654
14.0k
        enum ofperr error;
1655
14.0k
        uint64_t type;
1656
1657
14.0k
        error = ofpprop_pull(&properties, &payload, &type);
1658
14.0k
        if (error) {
1659
1.48k
            return error;
1660
1.48k
        }
1661
1662
12.5k
        switch (type) {
1663
64
        case OFPPROP_EXP(NTR_VENDOR_ID, NTRT_SELECTION_METHOD):
1664
9.95k
        case OFPPROP_EXP(NTR_COMPAT_VENDOR_ID, NTRT_SELECTION_METHOD):
1665
9.95k
            error = parse_group_prop_ntr_selection_method(&payload, group_type,
1666
9.95k
                                                          group_cmd, gp);
1667
9.95k
            break;
1668
1669
2.62k
        default:
1670
2.62k
            error = OFPPROP_UNKNOWN(false, "group", type);
1671
2.62k
            break;
1672
12.5k
        }
1673
1674
12.5k
        if (error) {
1675
9.14k
            return error;
1676
9.14k
        }
1677
12.5k
    }
1678
1679
5.42k
    return 0;
1680
16.0k
}
1681
1682
static int
1683
ofputil_decode_ofp11_group_desc_reply(struct ofputil_group_desc *gd,
1684
                                      struct ofpbuf *msg,
1685
                                      enum ofp_version version)
1686
904
{
1687
904
    struct ofp11_group_desc_stats *ogds;
1688
904
    size_t length;
1689
1690
904
    if (!msg->header) {
1691
421
        ofpraw_pull_assert(msg);
1692
421
    }
1693
1694
904
    if (!msg->size) {
1695
101
        return EOF;
1696
101
    }
1697
1698
803
    ogds = ofpbuf_try_pull(msg, sizeof *ogds);
1699
803
    if (!ogds) {
1700
0
        VLOG_WARN_RL(&rl, "OFPST11_GROUP_DESC reply has %"PRIu32" "
1701
0
                     "leftover bytes at end", msg->size);
1702
0
        return OFPERR_OFPBRC_BAD_LEN;
1703
0
    }
1704
803
    gd->type = ogds->type;
1705
803
    gd->group_id = ntohl(ogds->group_id);
1706
1707
803
    length = ntohs(ogds->length);
1708
803
    if (length < sizeof *ogds || length - sizeof *ogds > msg->size) {
1709
310
        VLOG_WARN_RL(&rl, "OFPST11_GROUP_DESC reply claims invalid "
1710
310
                     "length %"PRIuSIZE, length);
1711
310
        return OFPERR_OFPBRC_BAD_LEN;
1712
310
    }
1713
1714
493
    return ofputil_pull_ofp11_buckets(msg, length - sizeof *ogds, version,
1715
493
                                      &gd->buckets);
1716
803
}
1717
1718
static int
1719
ofputil_decode_ofp15_group_desc_reply(struct ofputil_group_desc *gd,
1720
                                      struct ofpbuf *msg,
1721
                                      enum ofp_version version)
1722
4.10k
{
1723
4.10k
    struct ofp15_group_desc_stats *ogds;
1724
4.10k
    uint16_t length, bucket_list_len;
1725
4.10k
    int error;
1726
1727
4.10k
    if (!msg->header) {
1728
3.49k
        ofpraw_pull_assert(msg);
1729
3.49k
    }
1730
1731
4.10k
    if (!msg->size) {
1732
76
        return EOF;
1733
76
    }
1734
1735
4.03k
    ogds = ofpbuf_try_pull(msg, sizeof *ogds);
1736
4.03k
    if (!ogds) {
1737
173
        VLOG_WARN_RL(&rl, "OFPST11_GROUP_DESC reply has %"PRIu32" "
1738
173
                     "leftover bytes at end", msg->size);
1739
173
        return OFPERR_OFPBRC_BAD_LEN;
1740
173
    }
1741
3.85k
    gd->type = ogds->type;
1742
3.85k
    gd->group_id = ntohl(ogds->group_id);
1743
1744
3.85k
    length = ntohs(ogds->length);
1745
3.85k
    if (length < sizeof *ogds || length - sizeof *ogds > msg->size) {
1746
1.05k
        VLOG_WARN_RL(&rl, "OFPST11_GROUP_DESC reply claims invalid "
1747
1.05k
                     "length %u", length);
1748
1.05k
        return OFPERR_OFPBRC_BAD_LEN;
1749
1.05k
    }
1750
1751
2.80k
    bucket_list_len = ntohs(ogds->bucket_list_len);
1752
2.80k
    if (length < bucket_list_len + sizeof *ogds) {
1753
193
        VLOG_WARN_RL(&rl, "OFPST11_GROUP_DESC reply claims invalid "
1754
193
                     "bucket list length %u", bucket_list_len);
1755
193
        return OFPERR_OFPBRC_BAD_LEN;
1756
193
    }
1757
2.61k
    error = ofputil_pull_ofp15_buckets(msg, bucket_list_len, version, gd->type,
1758
2.61k
                                       &gd->buckets);
1759
2.61k
    if (error) {
1760
949
        return error;
1761
949
    }
1762
1763
    /* By definition group desc messages don't have a group mod command.
1764
     * However, parse_group_prop_ntr_selection_method() checks to make sure
1765
     * that the command is OFPGC15_ADD or OFPGC15_DELETE to guard
1766
     * against group mod messages with other commands supplying
1767
     * a NTR selection method group experimenter property.
1768
     * Such properties are valid for group desc replies so
1769
     * claim that the group mod command is OFPGC15_ADD to
1770
     * satisfy the check in parse_group_prop_ntr_selection_method() */
1771
1.66k
    error = parse_ofp15_group_properties(
1772
1.66k
        msg, gd->type, OFPGC15_ADD, &gd->props,
1773
1.66k
        length - sizeof *ogds - bucket_list_len);
1774
1.66k
    if (error) {
1775
1.05k
        ofputil_uninit_group_desc(gd);
1776
1.05k
    }
1777
1.66k
    return error;
1778
2.61k
}
1779
1780
/* Converts a group description reply in 'msg' into an abstract
1781
 * ofputil_group_desc in 'gd'.
1782
 *
1783
 * Multiple group description replies can be packed into a single OpenFlow
1784
 * message.  Calling this function multiple times for a single 'msg' iterates
1785
 * through the replies.  The caller must initially leave 'msg''s layer pointers
1786
 * null and not modify them between calls.
1787
 *
1788
 * Returns 0 if successful, EOF if no replies were left in this 'msg',
1789
 * otherwise a positive errno value. */
1790
int
1791
ofputil_decode_group_desc_reply(struct ofputil_group_desc *gd,
1792
                                struct ofpbuf *msg, enum ofp_version version)
1793
5.01k
{
1794
5.01k
    ofputil_init_group_properties(&gd->props);
1795
1796
5.01k
    switch (version)
1797
5.01k
    {
1798
322
    case OFP11_VERSION:
1799
620
    case OFP12_VERSION:
1800
711
    case OFP13_VERSION:
1801
904
    case OFP14_VERSION:
1802
904
        return ofputil_decode_ofp11_group_desc_reply(gd, msg, version);
1803
1804
2.82k
    case OFP10_VERSION:
1805
4.10k
    case OFP15_VERSION:
1806
4.10k
        return ofputil_decode_ofp15_group_desc_reply(gd, msg, version);
1807
1808
0
    default:
1809
0
        OVS_NOT_REACHED();
1810
5.01k
    }
1811
5.01k
}
1812
1813
static void
1814
ofp_print_bucket_id(struct ds *s, const char *label, uint32_t bucket_id,
1815
                    enum ofp_version ofp_version)
1816
3.89k
{
1817
3.89k
    if (ofp_version > OFP10_VERSION && ofp_version < OFP15_VERSION) {
1818
2.00k
        return;
1819
2.00k
    }
1820
1821
1.88k
    ds_put_cstr(s, label);
1822
1823
1.88k
    switch (bucket_id) {
1824
99
    case OFPG15_BUCKET_FIRST:
1825
99
        ds_put_cstr(s, "first");
1826
99
        break;
1827
290
    case OFPG15_BUCKET_LAST:
1828
290
        ds_put_cstr(s, "last");
1829
290
        break;
1830
110
    case OFPG15_BUCKET_ALL:
1831
110
        ds_put_cstr(s, "all");
1832
110
        break;
1833
1.38k
    default:
1834
1.38k
        ds_put_format(s, "%"PRIu32, bucket_id);
1835
1.38k
        break;
1836
1.88k
    }
1837
1838
1.88k
    ds_put_char(s, ',');
1839
1.88k
}
1840
1841
void
1842
ofputil_bucket_format(struct ds * s, const struct ofputil_bucket *bucket,
1843
                      enum ofp11_group_type type, enum ofp_version ofp_version,
1844
                      const struct ofputil_port_map *port_map,
1845
                      const struct ofputil_table_map *table_map)
1846
2.68k
{
1847
2.68k
    ds_put_cstr(s, "bucket=");
1848
1849
2.68k
    ofp_print_bucket_id(s, "bucket_id:", bucket->bucket_id, ofp_version);
1850
2.68k
    if (bucket->weight != (type == OFPGT11_SELECT ? 1 : 0)) {
1851
1.71k
        ds_put_format(s, "weight:%"PRIu16",", bucket->weight);
1852
1.71k
    }
1853
2.68k
    if (bucket->watch_port != OFPP_NONE) {
1854
955
        ds_put_cstr(s, "watch_port:");
1855
955
        ofputil_format_port(bucket->watch_port, port_map, s);
1856
955
        ds_put_char(s, ',');
1857
955
    }
1858
2.68k
    if (bucket->watch_group != OFPG_ANY) {
1859
1.82k
        ds_put_format(s, "watch_group:%"PRIu32",", bucket->watch_group);
1860
1.82k
    }
1861
1862
2.68k
    ds_put_cstr(s, "actions=");
1863
2.68k
    struct ofpact_format_params fp = {
1864
2.68k
        .port_map = port_map,
1865
2.68k
        .table_map = table_map,
1866
2.68k
        .s = s,
1867
2.68k
    };
1868
2.68k
    ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, &fp);
1869
2.68k
}
1870
1871
void
1872
ofputil_group_format(struct ds *s, uint32_t group_id, uint8_t type,
1873
                     const struct ofputil_bucket *bucket,
1874
                     const struct ovs_list *p_buckets,
1875
                     const struct ofputil_group_props *props,
1876
                     enum ofp_version ofp_version, bool suppress_type,
1877
                     const struct ofputil_port_map *port_map,
1878
                     const struct ofputil_table_map *table_map)
1879
7.99k
{
1880
7.99k
    ds_put_format(s, "group_id=%"PRIu32, group_id);
1881
1882
7.99k
    if (!suppress_type) {
1883
6.79k
        static const char *type_str[] = { "all", "select", "indirect",
1884
6.79k
                                          "ff", "unknown" };
1885
6.79k
        ds_put_format(s, ",type=%s", type_str[type > 4 ? 4 : type]);
1886
6.79k
    }
1887
1888
7.99k
    ofputil_group_properties_format(props, s);
1889
1890
7.99k
    if (!bucket && !p_buckets) {
1891
0
        return;
1892
0
    }
1893
1894
7.99k
    ds_put_char(s, ',');
1895
1896
7.99k
    if (bucket) {
1897
0
        ofputil_bucket_format(s, bucket, type, ofp_version, NULL, NULL);
1898
7.99k
    } else {
1899
7.99k
        LIST_FOR_EACH (bucket, list_node, p_buckets) {
1900
2.68k
            ofputil_bucket_format(s, bucket, type, ofp_version,
1901
2.68k
                                port_map, table_map);
1902
2.68k
            ds_put_char(s, ',');
1903
2.68k
        }
1904
7.99k
        ds_chomp(s, ',');
1905
7.99k
    }
1906
7.99k
}
1907
1908
enum ofperr
1909
ofputil_group_desc_format(struct ds *s, const struct ofp_header *oh,
1910
                          const struct ofputil_port_map *port_map,
1911
                          const struct ofputil_table_map *table_map)
1912
3.91k
{
1913
3.91k
    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
1914
5.01k
    for (;;) {
1915
5.01k
        struct ofputil_group_desc gd;
1916
5.01k
        int retval;
1917
1918
5.01k
        retval = ofputil_decode_group_desc_reply(&gd, &b, oh->version);
1919
5.01k
        if (retval) {
1920
3.91k
            return retval != EOF ? retval : 0;
1921
3.91k
        }
1922
1923
1.09k
        ds_put_char(s, '\n');
1924
1.09k
        ds_put_char(s, ' ');
1925
1.09k
        ofputil_group_format(s, gd.group_id, gd.type, NULL, &gd.buckets,
1926
1.09k
                             &gd.props, oh->version, false,
1927
1.09k
                             port_map, table_map);
1928
1.09k
        ofputil_uninit_group_desc(&gd);
1929
1.09k
     }
1930
3.91k
}
1931
1932
void
1933
ofputil_uninit_group_mod(struct ofputil_group_mod *gm)
1934
20.7k
{
1935
20.7k
    ofputil_bucket_list_destroy(&gm->buckets);
1936
20.7k
    ofputil_group_properties_destroy(&gm->props);
1937
20.7k
}
1938
1939
static void
1940
bad_group_cmd(enum ofp15_group_mod_command cmd)
1941
0
{
1942
0
    const char *opt_version;
1943
0
    const char *version;
1944
0
    const char *cmd_str;
1945
1946
0
    switch (cmd) {
1947
0
    case OFPGC15_ADD:
1948
0
    case OFPGC15_MODIFY:
1949
0
    case OFPGC15_ADD_OR_MOD:
1950
0
    case OFPGC15_DELETE:
1951
0
        version = "1.1";
1952
0
        opt_version = "11";
1953
0
        break;
1954
1955
0
    case OFPGC15_INSERT_BUCKET:
1956
0
    case OFPGC15_REMOVE_BUCKET:
1957
0
        version = "1.5";
1958
0
        opt_version = "15";
1959
0
        break;
1960
1961
0
    default:
1962
0
        OVS_NOT_REACHED();
1963
0
    }
1964
1965
0
    switch (cmd) {
1966
0
    case OFPGC15_ADD:
1967
0
        cmd_str = "add-group";
1968
0
        break;
1969
1970
0
    case OFPGC15_MODIFY:
1971
0
    case OFPGC15_ADD_OR_MOD:
1972
0
        cmd_str = "mod-group";
1973
0
        break;
1974
1975
0
    case OFPGC15_DELETE:
1976
0
        cmd_str = "del-group";
1977
0
        break;
1978
1979
0
    case OFPGC15_INSERT_BUCKET:
1980
0
        cmd_str = "insert-bucket";
1981
0
        break;
1982
1983
0
    case OFPGC15_REMOVE_BUCKET:
1984
0
        cmd_str = "remove-bucket";
1985
0
        break;
1986
1987
0
    default:
1988
0
        OVS_NOT_REACHED();
1989
0
    }
1990
1991
0
    ovs_fatal(0, "%s needs OpenFlow %s or later (\'-O OpenFlow%s\')",
1992
0
              cmd_str, version, opt_version);
1993
1994
0
}
1995
1996
static struct ofpbuf *
1997
ofputil_encode_ofp11_group_mod(enum ofp_version ofp_version,
1998
                               const struct ofputil_group_mod *gm,
1999
                               const struct ovs_list *new_buckets,
2000
                               int group_existed)
2001
0
{
2002
0
    struct ofpbuf *b;
2003
0
    struct ofp11_group_mod *ogm;
2004
0
    size_t start_ogm;
2005
0
    struct ofputil_bucket *bucket;
2006
2007
0
    b = ofpraw_alloc(OFPRAW_OFPT11_GROUP_MOD, ofp_version, 0);
2008
0
    start_ogm = b->size;
2009
0
    ofpbuf_put_zeros(b, sizeof *ogm);
2010
2011
0
    uint16_t command = gm->command;
2012
0
    const struct ovs_list *buckets = &gm->buckets;
2013
0
    switch (gm->command) {
2014
0
    case OFPGC15_INSERT_BUCKET:
2015
0
    case OFPGC15_REMOVE_BUCKET:
2016
0
        if (!new_buckets) {
2017
0
            bad_group_cmd(gm->command);
2018
0
        }
2019
0
        command = OFPGC11_MODIFY;
2020
0
        buckets = new_buckets;
2021
0
        break;
2022
2023
0
    case OFPGC11_ADD_OR_MOD:
2024
0
        if (group_existed >= 0) {
2025
0
            command = group_existed ? OFPGC11_MODIFY : OFPGC11_ADD;
2026
0
        }
2027
0
        break;
2028
2029
0
    default:
2030
0
        break;
2031
0
    }
2032
0
    LIST_FOR_EACH (bucket, list_node, buckets) {
2033
0
        ofputil_put_ofp11_bucket(bucket, b, ofp_version);
2034
0
    }
2035
0
    ogm = ofpbuf_at_assert(b, start_ogm, sizeof *ogm);
2036
0
    ogm->command = htons(command);
2037
0
    ogm->type = gm->type;
2038
0
    ogm->group_id = htonl(gm->group_id);
2039
2040
0
    ofpmsg_update_length(b);
2041
2042
0
    return b;
2043
0
}
2044
2045
static struct ofpbuf *
2046
ofputil_encode_ofp15_group_mod(enum ofp_version ofp_version,
2047
                               const struct ofputil_group_mod *gm,
2048
                               int group_existed)
2049
0
{
2050
0
    struct ofpbuf *b;
2051
0
    struct ofp15_group_mod *ogm;
2052
0
    size_t start_ogm;
2053
0
    struct ofputil_bucket *bucket;
2054
0
    struct id_pool *bucket_ids = NULL;
2055
2056
0
    b = ofpraw_alloc((ofp_version == OFP10_VERSION
2057
0
                      ? OFPRAW_NXT_GROUP_MOD
2058
0
                      : OFPRAW_OFPT15_GROUP_MOD), ofp_version, 0);
2059
0
    start_ogm = b->size;
2060
0
    ofpbuf_put_zeros(b, sizeof *ogm);
2061
2062
0
    LIST_FOR_EACH (bucket, list_node, &gm->buckets) {
2063
0
        uint32_t bucket_id;
2064
2065
        /* Generate a bucket id if none was supplied */
2066
0
        if (bucket->bucket_id > OFPG15_BUCKET_MAX) {
2067
0
            if (!bucket_ids) {
2068
0
                const struct ofputil_bucket *bkt;
2069
2070
0
                bucket_ids = id_pool_create(0, OFPG15_BUCKET_MAX + 1);
2071
2072
                /* Mark all bucket_ids that are present in gm
2073
                 * as used in the pool. */
2074
0
                LIST_FOR_EACH_REVERSE (bkt, list_node, &gm->buckets) {
2075
0
                    if (bkt == bucket) {
2076
0
                        break;
2077
0
                    }
2078
0
                    if (bkt->bucket_id <= OFPG15_BUCKET_MAX) {
2079
0
                        id_pool_add(bucket_ids, bkt->bucket_id);
2080
0
                    }
2081
0
                }
2082
0
            }
2083
2084
0
            if (!id_pool_alloc_id(bucket_ids, &bucket_id)) {
2085
0
                OVS_NOT_REACHED();
2086
0
            }
2087
0
        } else {
2088
0
            bucket_id = bucket->bucket_id;
2089
0
        }
2090
2091
0
        ofputil_put_ofp15_bucket(bucket, bucket_id, gm->type, b, ofp_version);
2092
0
    }
2093
0
    ogm = ofpbuf_at_assert(b, start_ogm, sizeof *ogm);
2094
0
    ogm->command = htons(gm->command != OFPGC11_ADD_OR_MOD || group_existed < 0
2095
0
                         ? gm->command
2096
0
                         : group_existed ? OFPGC11_MODIFY : OFPGC11_ADD);
2097
0
    ogm->type = gm->type;
2098
0
    ogm->group_id = htonl(gm->group_id);
2099
0
    ogm->command_bucket_id = htonl(gm->command_bucket_id);
2100
0
    ogm->bucket_array_len = htons(b->size - start_ogm - sizeof *ogm);
2101
2102
    /* Add group properties */
2103
0
    if (gm->props.selection_method[0]) {
2104
0
        ofputil_put_group_prop_ntr_selection_method(ofp_version, &gm->props, b);
2105
0
    }
2106
2107
0
    id_pool_destroy(bucket_ids);
2108
0
    ofpmsg_update_length(b);
2109
0
    return b;
2110
0
}
2111
2112
/* Converts abstract group mod 'gm' into a message for OpenFlow version
2113
 * 'ofp_version' and returns the message.
2114
 *
2115
 * If 'new_buckets' is nonnull, it should point to the full set of new buckets
2116
 * that resulted from a OFPGC15_INSERT_BUCKET or OFPGC15_REMOVE_BUCKET command.
2117
 * It is needed to translate such group_mods into OpenFlow 1.1-1.4
2118
 * OFPGC11_MODIFY.  If it is null but needed for translation, then encoding the
2119
 * group_mod will print an error on stderr and exit().
2120
 *
2121
 * If 'group_existed' is nonnegative, it should specify whether the group
2122
 * existed before the command was executed.  If it is nonnegative, then it is
2123
 * used to translate OVS's nonstandard OFPGC11_ADD_OR_MOD into a standard
2124
 * command.  If it is negative, then OFPGC11_ADD_OR_MOD will be left as is. */
2125
struct ofpbuf *
2126
ofputil_encode_group_mod(enum ofp_version ofp_version,
2127
                         const struct ofputil_group_mod *gm,
2128
                         const struct ovs_list *new_buckets,
2129
                         int group_existed)
2130
0
{
2131
2132
0
    switch (ofp_version) {
2133
0
    case OFP11_VERSION:
2134
0
    case OFP12_VERSION:
2135
0
    case OFP13_VERSION:
2136
0
    case OFP14_VERSION:
2137
0
        return ofputil_encode_ofp11_group_mod(ofp_version, gm,
2138
0
                                              new_buckets, group_existed);
2139
2140
0
    case OFP10_VERSION:
2141
0
    case OFP15_VERSION:
2142
0
        return ofputil_encode_ofp15_group_mod(ofp_version, gm, group_existed);
2143
2144
0
    default:
2145
0
        OVS_NOT_REACHED();
2146
0
    }
2147
0
}
2148
2149
static enum ofperr
2150
ofputil_pull_ofp11_group_mod(struct ofpbuf *msg, enum ofp_version ofp_version,
2151
                             struct ofputil_group_mod *gm)
2152
14.9k
{
2153
14.9k
    const struct ofp11_group_mod *ogm;
2154
14.9k
    enum ofperr error;
2155
2156
14.9k
    ogm = ofpbuf_pull(msg, sizeof *ogm);
2157
14.9k
    gm->command = ntohs(ogm->command);
2158
14.9k
    gm->type = ogm->type;
2159
14.9k
    gm->group_id = ntohl(ogm->group_id);
2160
14.9k
    gm->command_bucket_id = OFPG15_BUCKET_ALL;
2161
2162
14.9k
    error = ofputil_pull_ofp11_buckets(msg, msg->size, ofp_version,
2163
14.9k
                                       &gm->buckets);
2164
2165
    /* OF1.3.5+ prescribes an error when an OFPGC_DELETE includes buckets. */
2166
14.9k
    if (!error
2167
14.9k
        && ofp_version >= OFP13_VERSION
2168
14.9k
        && gm->command == OFPGC11_DELETE
2169
14.9k
        && !ovs_list_is_empty(&gm->buckets)) {
2170
21
        error = OFPERR_OFPGMFC_INVALID_GROUP;
2171
21
        ofputil_bucket_list_destroy(&gm->buckets);
2172
21
    }
2173
2174
14.9k
    return error;
2175
14.9k
}
2176
2177
static enum ofperr
2178
ofputil_pull_ofp15_group_mod(struct ofpbuf *msg, enum ofp_version ofp_version,
2179
                             struct ofputil_group_mod *gm)
2180
38.2k
{
2181
38.2k
    const struct ofp15_group_mod *ogm;
2182
38.2k
    uint16_t bucket_list_len;
2183
38.2k
    enum ofperr error = OFPERR_OFPGMFC_BAD_BUCKET;
2184
2185
38.2k
    ogm = ofpbuf_pull(msg, sizeof *ogm);
2186
38.2k
    gm->command = ntohs(ogm->command);
2187
38.2k
    gm->type = ogm->type;
2188
38.2k
    gm->group_id = ntohl(ogm->group_id);
2189
2190
38.2k
    gm->command_bucket_id = ntohl(ogm->command_bucket_id);
2191
38.2k
    switch (gm->command) {
2192
21.8k
    case OFPGC15_REMOVE_BUCKET:
2193
21.8k
        if (gm->command_bucket_id == OFPG15_BUCKET_ALL) {
2194
405
            error = 0;
2195
405
        }
2196
        /* Fall through */
2197
22.8k
    case OFPGC15_INSERT_BUCKET:
2198
22.8k
        if (gm->command_bucket_id <= OFPG15_BUCKET_MAX ||
2199
22.8k
            gm->command_bucket_id == OFPG15_BUCKET_FIRST
2200
22.8k
            || gm->command_bucket_id == OFPG15_BUCKET_LAST) {
2201
22.4k
            error = 0;
2202
22.4k
        }
2203
22.8k
        break;
2204
2205
12.3k
    case OFPGC11_ADD:
2206
13.2k
    case OFPGC11_MODIFY:
2207
13.3k
    case OFPGC11_ADD_OR_MOD:
2208
13.3k
    case OFPGC11_DELETE:
2209
15.3k
    default:
2210
15.3k
        if (gm->command_bucket_id == OFPG15_BUCKET_ALL) {
2211
12.7k
            error = 0;
2212
12.7k
        }
2213
15.3k
        break;
2214
38.2k
    }
2215
38.2k
    if (error) {
2216
2.67k
        VLOG_WARN_RL(&rl,
2217
2.67k
                     "group command bucket id (%u) is out of range",
2218
2.67k
                     gm->command_bucket_id);
2219
2.67k
        return OFPERR_OFPGMFC_BAD_BUCKET;
2220
2.67k
    }
2221
2222
35.5k
    bucket_list_len = ntohs(ogm->bucket_array_len);
2223
35.5k
    if (bucket_list_len > msg->size) {
2224
2.30k
        return OFPERR_OFPBRC_BAD_LEN;
2225
2.30k
    }
2226
33.2k
    error = ofputil_pull_ofp15_buckets(msg, bucket_list_len, ofp_version,
2227
33.2k
                                       gm->type, &gm->buckets);
2228
33.2k
    if (error) {
2229
18.8k
        return error;
2230
18.8k
    }
2231
2232
14.3k
    error = parse_ofp15_group_properties(msg, gm->type, gm->command,
2233
14.3k
                                         &gm->props, msg->size);
2234
14.3k
    if (error) {
2235
9.58k
        ofputil_uninit_group_mod(gm);
2236
9.58k
    }
2237
14.3k
    return error;
2238
33.2k
}
2239
2240
static enum ofperr
2241
ofputil_check_group_mod(const struct ofputil_group_mod *gm)
2242
11.1k
{
2243
11.1k
    switch (gm->type) {
2244
1.39k
    case OFPGT11_INDIRECT:
2245
1.39k
        if (gm->command != OFPGC11_DELETE
2246
1.39k
            && !ovs_list_is_singleton(&gm->buckets) ) {
2247
106
            return OFPERR_OFPGMFC_INVALID_GROUP;
2248
106
        }
2249
1.28k
        break;
2250
1.96k
    case OFPGT11_ALL:
2251
6.62k
    case OFPGT11_SELECT:
2252
6.89k
    case OFPGT11_FF:
2253
6.89k
        break;
2254
2.83k
    default:
2255
2.83k
        return OFPERR_OFPGMFC_BAD_TYPE;
2256
11.1k
    }
2257
2258
8.17k
    switch (gm->command) {
2259
5.04k
    case OFPGC11_ADD:
2260
5.22k
    case OFPGC11_MODIFY:
2261
5.60k
    case OFPGC11_ADD_OR_MOD:
2262
6.32k
    case OFPGC11_DELETE:
2263
7.10k
    case OFPGC15_INSERT_BUCKET:
2264
7.10k
        break;
2265
508
    case OFPGC15_REMOVE_BUCKET:
2266
508
        if (!ovs_list_is_empty(&gm->buckets)) {
2267
17
            return OFPERR_OFPGMFC_BAD_BUCKET;
2268
17
        }
2269
491
        break;
2270
564
    default:
2271
564
        return OFPERR_OFPGMFC_BAD_COMMAND;
2272
8.17k
    }
2273
2274
7.59k
    struct ofputil_bucket *bucket;
2275
7.59k
    LIST_FOR_EACH (bucket, list_node, &gm->buckets) {
2276
3.36k
        if (bucket->weight && gm->type != OFPGT11_SELECT
2277
3.36k
            && gm->command != OFPGC15_INSERT_BUCKET) {
2278
223
            return OFPERR_OFPGMFC_INVALID_GROUP;
2279
223
        }
2280
2281
3.14k
        switch (gm->type) {
2282
644
        case OFPGT11_ALL:
2283
988
        case OFPGT11_INDIRECT:
2284
988
            if (ofputil_bucket_has_liveness(bucket)) {
2285
431
                return OFPERR_OFPGMFC_WATCH_UNSUPPORTED;
2286
431
            }
2287
557
            break;
2288
1.96k
        case OFPGT11_SELECT:
2289
1.96k
            break;
2290
198
        case OFPGT11_FF:
2291
198
            if (!ofputil_bucket_has_liveness(bucket)) {
2292
42
                return OFPERR_OFPGMFC_INVALID_GROUP;
2293
42
            }
2294
156
            break;
2295
156
        default:
2296
            /* Returning BAD TYPE to be consistent
2297
             * though gm->type has been checked already. */
2298
0
            return OFPERR_OFPGMFC_BAD_TYPE;
2299
3.14k
        }
2300
3.14k
    }
2301
2302
6.90k
    return 0;
2303
7.59k
}
2304
2305
/* Converts OpenFlow group mod message 'oh' into an abstract group mod in
2306
 * 'gm'.  Returns 0 if successful, otherwise an OpenFlow error code. */
2307
enum ofperr
2308
ofputil_decode_group_mod(const struct ofp_header *oh,
2309
                         struct ofputil_group_mod *gm)
2310
53.1k
{
2311
53.1k
    ofputil_init_group_properties(&gm->props);
2312
2313
53.1k
    enum ofp_version ofp_version = oh->version;
2314
53.1k
    struct ofpbuf msg = ofpbuf_const_initializer(oh, ntohs(oh->length));
2315
53.1k
    ofpraw_pull_assert(&msg);
2316
2317
53.1k
    enum ofperr err;
2318
53.1k
    switch (ofp_version) {
2319
1.93k
    case OFP11_VERSION:
2320
9.07k
    case OFP12_VERSION:
2321
9.39k
    case OFP13_VERSION:
2322
14.9k
    case OFP14_VERSION:
2323
14.9k
        err = ofputil_pull_ofp11_group_mod(&msg, ofp_version, gm);
2324
14.9k
        break;
2325
2326
22
    case OFP10_VERSION:
2327
38.2k
    case OFP15_VERSION:
2328
38.2k
        err = ofputil_pull_ofp15_group_mod(&msg, ofp_version, gm);
2329
38.2k
        break;
2330
2331
0
    default:
2332
0
        OVS_NOT_REACHED();
2333
53.1k
    }
2334
53.1k
    if (err) {
2335
41.9k
        return err;
2336
41.9k
    }
2337
2338
11.1k
    err = ofputil_check_group_mod(gm);
2339
11.1k
    if (err) {
2340
4.21k
        ofputil_uninit_group_mod(gm);
2341
4.21k
    }
2342
11.1k
    return err;
2343
53.1k
}
2344
2345
void
2346
ofputil_group_mod_format__(struct ds *s, enum ofp_version ofp_version,
2347
                           const struct ofputil_group_mod *gm,
2348
                           const struct ofputil_port_map *port_map,
2349
                           const struct ofputil_table_map *table_map)
2350
6.90k
{
2351
6.90k
    bool bucket_command = false;
2352
2353
6.90k
    ds_put_char(s, '\n');
2354
2355
6.90k
    ds_put_char(s, ' ');
2356
6.90k
    switch (gm->command) {
2357
4.42k
    case OFPGC11_ADD:
2358
4.42k
        ds_put_cstr(s, "ADD");
2359
4.42k
        break;
2360
2361
174
    case OFPGC11_MODIFY:
2362
174
        ds_put_cstr(s, "MOD");
2363
174
        break;
2364
2365
371
    case OFPGC11_ADD_OR_MOD:
2366
371
        ds_put_cstr(s, "ADD_OR_MOD");
2367
371
        break;
2368
2369
727
    case OFPGC11_DELETE:
2370
727
        ds_put_cstr(s, "DEL");
2371
727
        break;
2372
2373
713
    case OFPGC15_INSERT_BUCKET:
2374
713
        ds_put_cstr(s, "INSERT_BUCKET");
2375
713
        bucket_command = true;
2376
713
        break;
2377
2378
491
    case OFPGC15_REMOVE_BUCKET:
2379
491
        ds_put_cstr(s, "REMOVE_BUCKET");
2380
491
        bucket_command = true;
2381
491
        break;
2382
2383
0
    default:
2384
0
        ds_put_format(s, "cmd:%"PRIu16"", gm->command);
2385
6.90k
    }
2386
6.90k
    ds_put_char(s, ' ');
2387
2388
6.90k
    if (bucket_command) {
2389
1.20k
        ofp_print_bucket_id(s, "command_bucket_id:",
2390
1.20k
                            gm->command_bucket_id, ofp_version);
2391
1.20k
    }
2392
2393
6.90k
    ofputil_group_format(s, gm->group_id, gm->type, NULL, &gm->buckets,
2394
6.90k
                         &gm->props, ofp_version, bucket_command,
2395
6.90k
                         port_map, table_map);
2396
6.90k
}
2397
2398
enum ofperr
2399
ofputil_group_mod_format(struct ds *s, const struct ofp_header *oh,
2400
                         const struct ofputil_port_map *port_map,
2401
                         const struct ofputil_table_map *table_map)
2402
52.0k
{
2403
52.0k
    struct ofputil_group_mod gm;
2404
52.0k
    int error;
2405
2406
52.0k
    error = ofputil_decode_group_mod(oh, &gm);
2407
52.0k
    if (error) {
2408
45.7k
        return error;
2409
45.7k
    }
2410
6.26k
    ofputil_group_mod_format__(s, oh->version, &gm, port_map, table_map);
2411
6.26k
    ofputil_uninit_group_mod(&gm);
2412
6.26k
    return 0;
2413
52.0k
}