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