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