]> git.proxmox.com Git - mirror_ovs.git/blob - lib/ofp-group.c
ovsdb-idl: Fix iteration over tracked rows with no actual data.
[mirror_ovs.git] / lib / ofp-group.c
1 /*
2 * Copyright (c) 2008-2017, 2019 Nicira, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <config.h>
18 #include "openvswitch/ofp-group.h"
19 #include <errno.h>
20 #include "byte-order.h"
21 #include "id-pool.h"
22 #include "nx-match.h"
23 #include "openvswitch/ofp-actions.h"
24 #include "openvswitch/dynamic-string.h"
25 #include "openvswitch/ofp-msgs.h"
26 #include "openvswitch/ofp-parse.h"
27 #include "openvswitch/ofp-port.h"
28 #include "openvswitch/ofp-print.h"
29 #include "openvswitch/ofp-prop.h"
30 #include "openvswitch/ofpbuf.h"
31 #include "openvswitch/vlog.h"
32 #include "util.h"
33
34 VLOG_DEFINE_THIS_MODULE(ofp_group);
35
36 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
37
38 /* Stores the group id represented by 's' into '*group_idp'. 's' may be an
39 * integer or, for reserved group IDs, the standard OpenFlow name for the group
40 * (either "ANY" or "ALL").
41 *
42 * Returns true if successful, false if 's' is not a valid OpenFlow group ID or
43 * name. */
44 bool
45 ofputil_group_from_string(const char *s, uint32_t *group_idp)
46 {
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". */
64 void
65 ofputil_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". */
78 void
79 ofputil_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. */
98 void
99 ofputil_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 */
110 static struct ofputil_bucket *
111 ofputil_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'. */
127 void
128 ofputil_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. */
147 struct ofputil_bucket *
148 ofputil_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. */
167 bool
168 ofputil_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. */
188 struct ofputil_bucket *
189 ofputil_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. */
200 struct ofputil_bucket *
201 ofputil_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. */
215 struct ofpbuf *
216 ofputil_encode_group_stats_request(enum ofp_version ofp_version,
217 uint32_t group_id)
218 {
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;
227 }
228
229 void
230 ofputil_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. */
239 uint32_t
240 ofputil_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;
246 } else if (raw == OFPRAW_NXST_GROUP_DESC_REQUEST ||
247 raw == OFPRAW_OFPST15_GROUP_DESC_REQUEST) {
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. */
262 struct ofpbuf *
263 ofputil_encode_group_desc_request(enum ofp_version ofp_version,
264 uint32_t group_id)
265 {
266 struct ofpbuf *request;
267
268 switch (ofp_version) {
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;
276 case OFP10_VERSION:
277 case OFP15_VERSION: {
278 struct ofp15_group_desc_request *req;
279 request = ofpraw_alloc((ofp_version == OFP10_VERSION
280 ? OFPRAW_NXST_GROUP_DESC_REQUEST
281 : OFPRAW_OFPST15_GROUP_DESC_REQUEST),
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
294
295 enum ofperr
296 ofputil_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
306 static void
307 ofputil_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
318 static void
319 ofputil_group_stats_to_ofp11(const struct ofputil_group_stats *gs,
320 struct ofp11_group_stats *gs11, size_t length,
321 struct ofp11_bucket_counter bucket_cnts[])
322 {
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
332 static void
333 ofputil_group_stats_to_ofp13(const struct ofputil_group_stats *gs,
334 struct ofp13_group_stats *gs13, size_t length,
335 struct ofp11_bucket_counter bucket_cnts[])
336 {
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(). */
346 void
347 ofputil_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
368 case OFP10_VERSION:
369 case OFP13_VERSION:
370 case OFP14_VERSION:
371 case OFP15_VERSION: {
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
381 default:
382 OVS_NOT_REACHED();
383 }
384 }
385
386 /* Returns an OpenFlow group features request for OpenFlow version
387 * 'ofp_version'. */
388 struct ofpbuf *
389 ofputil_encode_group_features_request(enum ofp_version ofp_version)
390 {
391 return ofpraw_alloc((ofp_version < OFP12_VERSION
392 ? OFPRAW_NXST_GROUP_FEATURES_REQUEST
393 : OFPRAW_OFPST12_GROUP_FEATURES_REQUEST),
394 ofp_version, 0);
395 }
396
397 /* Returns a OpenFlow message that encodes 'features' properly as a reply to
398 * group features request 'request'. */
399 struct ofpbuf *
400 ofputil_encode_group_features_reply(
401 const struct ofputil_group_features *features,
402 const struct ofp_header *request)
403 {
404 struct ofpbuf *reply = ofpraw_alloc_stats_reply(request, 0);
405 struct ofp12_group_features_stats *ogf
406 = ofpbuf_put_zeros(reply, sizeof *ogf);
407 ogf->types = htonl(features->types);
408 ogf->capabilities = htonl(features->capabilities);
409 for (int i = 0; i < OFPGT12_N_TYPES; i++) {
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'. */
419 void
420 ofputil_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
435 static const char *
436 group_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
447 enum ofperr
448 ofputil_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
474 /* Parse a group status request message into a 32 bit OpenFlow 1.1
475 * group ID and stores the latter in '*group_id'.
476 * Returns 0 if successful, otherwise an OFPERR_* number. */
477 enum ofperr
478 ofputil_decode_group_stats_request(const struct ofp_header *request,
479 uint32_t *group_id)
480 {
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. */
497 int
498 ofputil_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;
524 } else if (raw == OFPRAW_NXST_GROUP_REPLY ||
525 raw == OFPRAW_OFPST13_GROUP_REPLY) {
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
575
576 enum ofperr
577 ofputil_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
593 enum ofperr
594 ofputil_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
637 static char * OVS_WARN_UNUSED_RESULT
638 parse_bucket_str(struct ofputil_bucket *bucket, char *str_,
639 const struct ofputil_port_map *port_map,
640 const struct ofputil_table_map *table_map,
641 uint8_t group_type, enum ofputil_protocol *usable_protocols)
642 {
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 && bucket->watch_port != OFPP_CONTROLLER)) {
665 error = xasprintf("%s: invalid watch_port", value);
666 }
667 } else if (!strcasecmp(key, "watch_group")) {
668 error = str_to_u32(value, &bucket->watch_group);
669 if (!error && bucket->watch_group > OFPG_MAX) {
670 error = xasprintf("invalid watch_group id %"PRIu32,
671 bucket->watch_group);
672 }
673 } else if (!strcasecmp(key, "bucket_id")) {
674 error = str_to_u32(value, &bucket->bucket_id);
675 if (!error && bucket->bucket_id > OFPG15_BUCKET_MAX) {
676 error = xasprintf("invalid bucket_id id %"PRIu32,
677 bucket->bucket_id);
678 }
679 *usable_protocols &= OFPUTIL_P_OF10_ANY | OFPUTIL_P_OF15_UP;
680 } else if (!strcasecmp(key, "action") || !strcasecmp(key, "actions")) {
681 ds_put_format(&actions, "%s,", value);
682 } else {
683 ds_put_format(&actions, "%s(%s),", key, value);
684 }
685
686 if (error) {
687 ds_destroy(&actions);
688 return error;
689 }
690 }
691
692 if (!actions.length) {
693 return xstrdup("bucket must specify actions");
694 }
695 if (group_type == OFPGT11_FF && !ofputil_bucket_has_liveness(bucket)) {
696 return xstrdup("fast failover bucket requires watch_port or "
697 "watch_group");
698 }
699 ds_chomp(&actions, ',');
700
701 ofpbuf_init(&ofpacts, 0);
702 struct ofpact_parse_params pp = {
703 .port_map = port_map,
704 .table_map = table_map,
705 .ofpacts = &ofpacts,
706 .usable_protocols = usable_protocols,
707 };
708 error = ofpacts_parse_actions(ds_cstr(&actions), &pp);
709 ds_destroy(&actions);
710 if (error) {
711 ofpbuf_uninit(&ofpacts);
712 return error;
713 }
714 bucket->ofpacts = ofpacts.data;
715 bucket->ofpacts_len = ofpacts.size;
716
717 return NULL;
718 }
719
720 static char * OVS_WARN_UNUSED_RESULT
721 parse_select_group_field(char *s, const struct ofputil_port_map *port_map,
722 struct field_array *fa,
723 enum ofputil_protocol *usable_protocols)
724 {
725 char *name, *value_str;
726
727 while (ofputil_parse_key_value(&s, &name, &value_str)) {
728 const struct mf_field *mf = mf_from_name(name);
729
730 if (mf) {
731 char *error;
732 union mf_value value;
733
734 if (bitmap_is_set(fa->used.bm, mf->id)) {
735 return xasprintf("%s: duplicate field", name);
736 }
737
738 if (*value_str) {
739 error = mf_parse_value(mf, value_str, port_map, &value);
740 if (error) {
741 return error;
742 }
743
744 /* The mask cannot be all-zeros */
745 if (!mf_is_tun_metadata(mf) &&
746 is_all_zeros(&value, mf->n_bytes)) {
747 return xasprintf("%s: values are wildcards here "
748 "and must not be all-zeros", s);
749 }
750
751 /* The values parsed are masks for fields used
752 * by the selection method */
753 if (!mf_is_mask_valid(mf, &value)) {
754 return xasprintf("%s: invalid mask for field %s",
755 value_str, mf->name);
756 }
757 } else {
758 memset(&value, 0xff, mf->n_bytes);
759 }
760
761 field_array_set(mf->id, &value, fa);
762
763 if (is_all_ones(&value, mf->n_bytes)) {
764 *usable_protocols &= mf->usable_protocols_exact;
765 } else if (mf->usable_protocols_bitwise == mf->usable_protocols_cidr
766 || ip_is_cidr(value.be32)) {
767 *usable_protocols &= mf->usable_protocols_cidr;
768 } else {
769 *usable_protocols &= mf->usable_protocols_bitwise;
770 }
771 } else {
772 return xasprintf("%s: unknown field %s", s, name);
773 }
774 }
775
776 return NULL;
777 }
778
779 static char * OVS_WARN_UNUSED_RESULT
780 parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, int command,
781 char *string,
782 const struct ofputil_port_map *port_map,
783 const struct ofputil_table_map *table_map,
784 enum ofputil_protocol *usable_protocols)
785 {
786 enum {
787 F_GROUP_TYPE = 1 << 0,
788 F_BUCKETS = 1 << 1,
789 F_COMMAND_BUCKET_ID = 1 << 2,
790 F_COMMAND_BUCKET_ID_ALL = 1 << 3,
791 } fields;
792 bool had_type = false;
793 bool had_command_bucket_id = false;
794 struct ofputil_bucket *bucket;
795 char *error = NULL;
796
797 *usable_protocols = OFPUTIL_P_ANY;
798
799 if (command == -2) {
800 size_t len;
801
802 string += strspn(string, " \t\r\n"); /* Skip white space. */
803 len = strcspn(string, ", \t\r\n"); /* Get length of the first token. */
804
805 if (!strncmp(string, "add", len)) {
806 command = OFPGC11_ADD;
807 } else if (!strncmp(string, "delete", len)) {
808 command = OFPGC11_DELETE;
809 } else if (!strncmp(string, "modify", len)) {
810 command = OFPGC11_MODIFY;
811 } else if (!strncmp(string, "add_or_mod", len)) {
812 command = OFPGC11_ADD_OR_MOD;
813 } else if (!strncmp(string, "insert_bucket", len)) {
814 command = OFPGC15_INSERT_BUCKET;
815 } else if (!strncmp(string, "remove_bucket", len)) {
816 command = OFPGC15_REMOVE_BUCKET;
817 } else {
818 len = 0;
819 command = OFPGC11_ADD;
820 }
821 string += len;
822 }
823
824 switch (command) {
825 case OFPGC11_ADD:
826 fields = F_GROUP_TYPE | F_BUCKETS;
827 break;
828
829 case OFPGC11_DELETE:
830 fields = 0;
831 break;
832
833 case OFPGC11_MODIFY:
834 fields = F_GROUP_TYPE | F_BUCKETS;
835 break;
836
837 case OFPGC11_ADD_OR_MOD:
838 fields = F_GROUP_TYPE | F_BUCKETS;
839 break;
840
841 case OFPGC15_INSERT_BUCKET:
842 fields = F_BUCKETS | F_COMMAND_BUCKET_ID;
843 *usable_protocols &= OFPUTIL_P_OF10_ANY | OFPUTIL_P_OF15_UP;
844 break;
845
846 case OFPGC15_REMOVE_BUCKET:
847 fields = F_COMMAND_BUCKET_ID | F_COMMAND_BUCKET_ID_ALL;
848 *usable_protocols &= OFPUTIL_P_OF10_ANY | OFPUTIL_P_OF15_UP;
849 break;
850
851 default:
852 OVS_NOT_REACHED();
853 }
854
855 memset(gm, 0, sizeof *gm);
856 gm->command = command;
857 gm->group_id = OFPG_ANY;
858 gm->command_bucket_id = OFPG15_BUCKET_ALL;
859 ovs_list_init(&gm->buckets);
860 if (command == OFPGC11_DELETE && string[0] == '\0') {
861 gm->group_id = OFPG_ALL;
862 return NULL;
863 }
864
865 /* Strip the buckets off the end of 'string', if there are any, saving a
866 * pointer for later. We want to parse the buckets last because the bucket
867 * type influences bucket defaults. */
868 char *bkt_str = strstr(string, "bucket=");
869 if (bkt_str) {
870 if (!(fields & F_BUCKETS)) {
871 error = xstrdup("bucket is not needed");
872 goto out;
873 }
874 *bkt_str = '\0';
875 }
876
877 /* Parse everything before the buckets. */
878 char *pos = string;
879 char *name, *value;
880 while (ofputil_parse_key_value(&pos, &name, &value)) {
881 if (!strcmp(name, "command_bucket_id")) {
882 if (!(fields & F_COMMAND_BUCKET_ID)) {
883 error = xstrdup("command bucket id is not needed");
884 goto out;
885 }
886 if (!strcmp(value, "all")) {
887 gm->command_bucket_id = OFPG15_BUCKET_ALL;
888 } else if (!strcmp(value, "first")) {
889 gm->command_bucket_id = OFPG15_BUCKET_FIRST;
890 } else if (!strcmp(value, "last")) {
891 gm->command_bucket_id = OFPG15_BUCKET_LAST;
892 } else {
893 error = str_to_u32(value, &gm->command_bucket_id);
894 if (error) {
895 goto out;
896 }
897 if (gm->command_bucket_id > OFPG15_BUCKET_MAX
898 && (gm->command_bucket_id != OFPG15_BUCKET_FIRST
899 && gm->command_bucket_id != OFPG15_BUCKET_LAST
900 && gm->command_bucket_id != OFPG15_BUCKET_ALL)) {
901 error = xasprintf("invalid command bucket id %"PRIu32,
902 gm->command_bucket_id);
903 goto out;
904 }
905 }
906 if (gm->command_bucket_id == OFPG15_BUCKET_ALL
907 && !(fields & F_COMMAND_BUCKET_ID_ALL)) {
908 error = xstrdup("command_bucket_id=all is not permitted");
909 goto out;
910 }
911 had_command_bucket_id = true;
912 } else if (!strcmp(name, "group_id")) {
913 if(!strcmp(value, "all")) {
914 gm->group_id = OFPG_ALL;
915 } else {
916 error = str_to_u32(value, &gm->group_id);
917 if (error) {
918 goto out;
919 }
920 if (gm->group_id != OFPG_ALL && gm->group_id > OFPG_MAX) {
921 error = xasprintf("invalid group id %"PRIu32,
922 gm->group_id);
923 goto out;
924 }
925 }
926 } else if (!strcmp(name, "type")){
927 if (!(fields & F_GROUP_TYPE)) {
928 error = xstrdup("type is not needed");
929 goto out;
930 }
931 if (!strcmp(value, "all")) {
932 gm->type = OFPGT11_ALL;
933 } else if (!strcmp(value, "select")) {
934 gm->type = OFPGT11_SELECT;
935 } else if (!strcmp(value, "indirect")) {
936 gm->type = OFPGT11_INDIRECT;
937 } else if (!strcmp(value, "ff") ||
938 !strcmp(value, "fast_failover")) {
939 gm->type = OFPGT11_FF;
940 } else {
941 error = xasprintf("invalid group type %s", value);
942 goto out;
943 }
944 had_type = true;
945 } else if (!strcmp(name, "selection_method")) {
946 if (!(fields & F_GROUP_TYPE)) {
947 error = xstrdup("selection method is not needed");
948 goto out;
949 }
950 if (strlen(value) >= NTR_MAX_SELECTION_METHOD_LEN) {
951 error = xasprintf("selection method is longer than %u"
952 " bytes long",
953 NTR_MAX_SELECTION_METHOD_LEN - 1);
954 goto out;
955 }
956 memset(gm->props.selection_method, '\0',
957 NTR_MAX_SELECTION_METHOD_LEN);
958 strcpy(gm->props.selection_method, value);
959 *usable_protocols &= OFPUTIL_P_OF10_ANY | OFPUTIL_P_OF15_UP;
960 } else if (!strcmp(name, "selection_method_param")) {
961 if (!(fields & F_GROUP_TYPE)) {
962 error = xstrdup("selection method param is not needed");
963 goto out;
964 }
965 error = str_to_u64(value, &gm->props.selection_method_param);
966 if (error) {
967 goto out;
968 }
969 *usable_protocols &= OFPUTIL_P_OF10_ANY | OFPUTIL_P_OF15_UP;
970 } else if (!strcmp(name, "fields")) {
971 if (!(fields & F_GROUP_TYPE)) {
972 error = xstrdup("fields are not needed");
973 goto out;
974 }
975 error = parse_select_group_field(value, port_map,
976 &gm->props.fields,
977 usable_protocols);
978 if (error) {
979 goto out;
980 }
981 *usable_protocols &= OFPUTIL_P_OF10_ANY | OFPUTIL_P_OF15_UP;
982 } else {
983 error = xasprintf("unknown keyword %s", name);
984 goto out;
985 }
986 }
987 if (gm->group_id == OFPG_ANY) {
988 error = xstrdup("must specify a group_id");
989 goto out;
990 }
991 if (fields & F_GROUP_TYPE && !had_type) {
992 error = xstrdup("must specify a type");
993 goto out;
994 }
995
996 /* Exclude fields for non "hash" selection method. */
997 if (strcmp(gm->props.selection_method, "hash") &&
998 gm->props.fields.values_size) {
999 error = xstrdup("fields may only be specified with "
1000 "\"selection_method=hash\"");
1001 goto out;
1002 }
1003 /* Exclude selection_method_param if no selection_method is given. */
1004 if (gm->props.selection_method[0] == 0
1005 && gm->props.selection_method_param != 0) {
1006 error = xstrdup("selection_method_param is only allowed with "
1007 "\"selection_method\"");
1008 goto out;
1009 }
1010 if (fields & F_COMMAND_BUCKET_ID) {
1011 if (!(fields & F_COMMAND_BUCKET_ID_ALL || had_command_bucket_id)) {
1012 error = xstrdup("must specify a command bucket id");
1013 goto out;
1014 }
1015 } else if (had_command_bucket_id) {
1016 error = xstrdup("command bucket id is not needed");
1017 goto out;
1018 }
1019
1020 /* Now parse the buckets, if any. */
1021 while (bkt_str) {
1022 char *next_bkt_str;
1023
1024 bkt_str = strchr(bkt_str + 1, '=');
1025 if (!bkt_str) {
1026 error = xstrdup("must specify bucket content");
1027 goto out;
1028 }
1029 bkt_str++;
1030
1031 next_bkt_str = strstr(bkt_str, "bucket=");
1032 if (next_bkt_str) {
1033 *next_bkt_str = '\0';
1034 }
1035
1036 bucket = xzalloc(sizeof(struct ofputil_bucket));
1037 error = parse_bucket_str(bucket, bkt_str, port_map, table_map,
1038 gm->type, usable_protocols);
1039 if (error) {
1040 free(bucket);
1041 goto out;
1042 }
1043 ovs_list_push_back(&gm->buckets, &bucket->list_node);
1044
1045 if (gm->command != OFPGC15_INSERT_BUCKET
1046 && 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 */
1068 char * OVS_WARN_UNUSED_RESULT
1069 parse_ofp_group_mod_str(struct ofputil_group_mod *gm, int command,
1070 const char *str_,
1071 const struct ofputil_port_map *port_map,
1072 const struct ofputil_table_map *table_map,
1073 enum ofputil_protocol *usable_protocols)
1074 {
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 */
1086 char * OVS_WARN_UNUSED_RESULT
1087 parse_ofp_group_mod_file(const char *file_name,
1088 const struct ofputil_port_map *port_map,
1089 const struct ofputil_table_map *table_map,
1090 int command,
1091 struct ofputil_group_mod **gms, size_t *n_gms,
1092 enum ofputil_protocol *usable_protocols)
1093 {
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;
1111 *usable_protocols = OFPUTIL_P_ANY;
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
1158 static void
1159 ofputil_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
1176 static void
1177 ofputil_put_ofp15_bucket(const struct ofputil_bucket *bucket,
1178 uint32_t bucket_id, enum ofp11_group_type group_type,
1179 struct ofpbuf *openflow, enum ofp_version ofp_version)
1180 {
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 || bucket->weight) {
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
1209 static void
1210 ofputil_put_group_prop_ntr_selection_method(enum ofp_version ofp_version,
1211 const struct ofputil_group_props *gp,
1212 struct ofpbuf *openflow)
1213 {
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
1229 static void
1230 ofputil_append_ofp11_group_desc_reply(const struct ofputil_group_desc *gds,
1231 const struct ovs_list *buckets,
1232 struct ovs_list *replies,
1233 enum ofp_version version)
1234 {
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
1253 static void
1254 ofputil_append_ofp15_group_desc_reply(const struct ofputil_group_desc *gds,
1255 const struct ovs_list *buckets,
1256 struct ovs_list *replies,
1257 enum ofp_version version)
1258 {
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(). */
1290 void
1291 ofputil_append_group_desc_reply(const struct ofputil_group_desc *gds,
1292 const struct ovs_list *buckets,
1293 struct ovs_list *replies)
1294 {
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
1306 case OFP10_VERSION:
1307 case OFP15_VERSION:
1308 ofputil_append_ofp15_group_desc_reply(gds, buckets, replies, version);
1309 break;
1310
1311 default:
1312 OVS_NOT_REACHED();
1313 }
1314 }
1315
1316 static enum ofperr
1317 ofputil_pull_ofp11_buckets(struct ofpbuf *msg, size_t buckets_length,
1318 enum ofp_version version, struct ovs_list *buckets)
1319 {
1320 struct ofp11_bucket *ob;
1321 uint32_t bucket_id = 0;
1322
1323 ovs_list_init(buckets);
1324 while (buckets_length > 0) {
1325 struct ofputil_bucket *bucket;
1326 struct ofpbuf ofpacts;
1327 enum ofperr error;
1328 size_t ob_len;
1329
1330 ob = (buckets_length >= sizeof *ob
1331 ? ofpbuf_try_pull(msg, sizeof *ob)
1332 : NULL);
1333 if (!ob) {
1334 VLOG_WARN_RL(&rl, "buckets end with %"PRIuSIZE" leftover bytes",
1335 buckets_length);
1336 ofputil_bucket_list_destroy(buckets);
1337 return OFPERR_OFPGMFC_BAD_BUCKET;
1338 }
1339
1340 ob_len = ntohs(ob->len);
1341 if (ob_len < sizeof *ob) {
1342 VLOG_WARN_RL(&rl, "OpenFlow message bucket length "
1343 "%"PRIuSIZE" is not valid", ob_len);
1344 ofputil_bucket_list_destroy(buckets);
1345 return OFPERR_OFPGMFC_BAD_BUCKET;
1346 } else if (ob_len > buckets_length) {
1347 VLOG_WARN_RL(&rl, "OpenFlow message bucket length %"PRIuSIZE" "
1348 "exceeds remaining buckets data size %"PRIuSIZE,
1349 ob_len, buckets_length);
1350 ofputil_bucket_list_destroy(buckets);
1351 return OFPERR_OFPGMFC_BAD_BUCKET;
1352 }
1353 buckets_length -= ob_len;
1354
1355 ofpbuf_init(&ofpacts, 0);
1356 error = ofpacts_pull_openflow_actions(msg, ob_len - sizeof *ob,
1357 version, NULL, NULL, &ofpacts);
1358 if (error) {
1359 ofpbuf_uninit(&ofpacts);
1360 ofputil_bucket_list_destroy(buckets);
1361 return error;
1362 }
1363
1364 bucket = xzalloc(sizeof *bucket);
1365 bucket->weight = ntohs(ob->weight);
1366 error = ofputil_port_from_ofp11(ob->watch_port, &bucket->watch_port);
1367 if (error) {
1368 ofpbuf_uninit(&ofpacts);
1369 ofputil_bucket_list_destroy(buckets);
1370 free(bucket);
1371 return OFPERR_OFPGMFC_BAD_WATCH;
1372 }
1373 bucket->watch_group = ntohl(ob->watch_group);
1374 bucket->bucket_id = bucket_id++;
1375
1376 bucket->ofpacts = ofpbuf_steal_data(&ofpacts);
1377 bucket->ofpacts_len = ofpacts.size;
1378 ovs_list_push_back(buckets, &bucket->list_node);
1379 }
1380
1381 return 0;
1382 }
1383
1384 static enum ofperr
1385 ofputil_pull_ofp15_buckets(struct ofpbuf *msg, size_t buckets_length,
1386 enum ofp_version version, uint8_t group_type,
1387 struct ovs_list *buckets)
1388 {
1389 ovs_list_init(buckets);
1390 while (buckets_length > 0) {
1391 struct ofputil_bucket *bucket = NULL;
1392 struct ofpbuf ofpacts;
1393 enum ofperr err = OFPERR_OFPGMFC_BAD_BUCKET;
1394 size_t ob_len, actions_len, properties_len;
1395 ovs_be32 watch_port = ofputil_port_to_ofp11(OFPP_ANY);
1396 ovs_be32 watch_group = htonl(OFPG_ANY);
1397 ovs_be16 weight = htons(group_type == OFPGT11_SELECT ? 1 : 0);
1398
1399 ofpbuf_init(&ofpacts, 0);
1400
1401 struct ofp15_bucket *ob = ofpbuf_try_pull(msg, sizeof *ob);
1402 if (!ob) {
1403 VLOG_WARN_RL(&rl, "buckets end with %"PRIuSIZE
1404 " leftover bytes", buckets_length);
1405 goto err;
1406 }
1407
1408 ob_len = ntohs(ob->len);
1409 actions_len = ntohs(ob->action_array_len);
1410
1411 if (ob_len < sizeof *ob) {
1412 VLOG_WARN_RL(&rl, "OpenFlow message bucket length "
1413 "%"PRIuSIZE" is not valid", ob_len);
1414 goto err;
1415 } else if (ob_len > buckets_length) {
1416 VLOG_WARN_RL(&rl, "OpenFlow message bucket length "
1417 "%"PRIuSIZE" exceeds remaining buckets data size %"
1418 PRIuSIZE, ob_len, buckets_length);
1419 goto err;
1420 } else if (actions_len > ob_len - sizeof *ob) {
1421 VLOG_WARN_RL(&rl, "OpenFlow message bucket actions "
1422 "length %"PRIuSIZE" exceeds remaining bucket "
1423 "data size %"PRIuSIZE, actions_len,
1424 ob_len - sizeof *ob);
1425 goto err;
1426 }
1427 buckets_length -= ob_len;
1428
1429 err = ofpacts_pull_openflow_actions(msg, actions_len, version,
1430 NULL, NULL, &ofpacts);
1431 if (err) {
1432 goto err;
1433 }
1434
1435 properties_len = ob_len - sizeof *ob - actions_len;
1436 struct ofpbuf properties = ofpbuf_const_initializer(
1437 ofpbuf_pull(msg, properties_len), properties_len);
1438 while (properties.size > 0) {
1439 struct ofpbuf payload;
1440 uint64_t type;
1441
1442 err = ofpprop_pull(&properties, &payload, &type);
1443 if (err) {
1444 goto err;
1445 }
1446
1447 switch (type) {
1448 case OFPGBPT15_WEIGHT:
1449 err = ofpprop_parse_be16(&payload, &weight);
1450 break;
1451
1452 case OFPGBPT15_WATCH_PORT:
1453 err = ofpprop_parse_be32(&payload, &watch_port);
1454 break;
1455
1456 case OFPGBPT15_WATCH_GROUP:
1457 err = ofpprop_parse_be32(&payload, &watch_group);
1458 break;
1459
1460 default:
1461 err = OFPPROP_UNKNOWN(false, "group bucket", type);
1462 break;
1463 }
1464
1465 if (err) {
1466 goto err;
1467 }
1468 }
1469
1470 bucket = xzalloc(sizeof *bucket);
1471
1472 bucket->weight = ntohs(weight);
1473 err = ofputil_port_from_ofp11(watch_port, &bucket->watch_port);
1474 if (err) {
1475 err = OFPERR_OFPGMFC_BAD_WATCH;
1476 goto err;
1477 }
1478 bucket->watch_group = ntohl(watch_group);
1479 bucket->bucket_id = ntohl(ob->bucket_id);
1480 if (bucket->bucket_id > OFPG15_BUCKET_MAX) {
1481 VLOG_WARN_RL(&rl, "bucket id (%u) is out of range",
1482 bucket->bucket_id);
1483 err = OFPERR_OFPGMFC_BAD_BUCKET;
1484 goto err;
1485 }
1486
1487 bucket->ofpacts = ofpbuf_steal_data(&ofpacts);
1488 bucket->ofpacts_len = ofpacts.size;
1489 ovs_list_push_back(buckets, &bucket->list_node);
1490
1491 continue;
1492
1493 err:
1494 free(bucket);
1495 ofpbuf_uninit(&ofpacts);
1496 ofputil_bucket_list_destroy(buckets);
1497 return err;
1498 }
1499
1500 if (ofputil_bucket_check_duplicate_id(buckets)) {
1501 VLOG_WARN_RL(&rl, "Duplicate bucket id");
1502 ofputil_bucket_list_destroy(buckets);
1503 return OFPERR_OFPGMFC_BAD_BUCKET;
1504 }
1505
1506 return 0;
1507 }
1508
1509 static void
1510 ofputil_init_group_properties(struct ofputil_group_props *gp)
1511 {
1512 memset(gp, 0, sizeof *gp);
1513 }
1514
1515 void
1516 ofputil_group_properties_copy(struct ofputil_group_props *to,
1517 const struct ofputil_group_props *from)
1518 {
1519 *to = *from;
1520 to->fields.values = xmemdup(from->fields.values, from->fields.values_size);
1521 }
1522
1523 void
1524 ofputil_group_properties_destroy(struct ofputil_group_props *gp)
1525 {
1526 free(gp->fields.values);
1527 }
1528
1529 static enum ofperr
1530 parse_group_prop_ntr_selection_method(struct ofpbuf *payload,
1531 enum ofp11_group_type group_type,
1532 enum ofp15_group_mod_command group_cmd,
1533 struct ofputil_group_props *gp)
1534 {
1535 struct ntr_group_prop_selection_method *prop = payload->data;
1536 size_t fields_len, method_len;
1537 enum ofperr error;
1538
1539 switch (group_type) {
1540 case OFPGT11_SELECT:
1541 break;
1542 case OFPGT11_ALL:
1543 case OFPGT11_INDIRECT:
1544 case OFPGT11_FF:
1545 OFPPROP_LOG(&rl, false, "ntr selection method property is "
1546 "only allowed for select groups");
1547 return OFPERR_OFPBPC_BAD_VALUE;
1548 default:
1549 return OFPERR_OFPGMFC_BAD_TYPE;
1550 }
1551
1552 switch (group_cmd) {
1553 case OFPGC15_ADD:
1554 case OFPGC15_MODIFY:
1555 case OFPGC15_ADD_OR_MOD:
1556 break;
1557 case OFPGC15_DELETE:
1558 case OFPGC15_INSERT_BUCKET:
1559 case OFPGC15_REMOVE_BUCKET:
1560 OFPPROP_LOG(&rl, false, "ntr selection method property is "
1561 "only allowed for add and delete group modifications");
1562 return OFPERR_OFPBPC_BAD_VALUE;
1563 default:
1564 return OFPERR_OFPGMFC_BAD_COMMAND;
1565 }
1566
1567 if (payload->size < sizeof *prop) {
1568 OFPPROP_LOG(&rl, false, "ntr selection method property "
1569 "length %u is not valid", payload->size);
1570 return OFPERR_OFPBPC_BAD_LEN;
1571 }
1572
1573 method_len = strnlen(prop->selection_method, NTR_MAX_SELECTION_METHOD_LEN);
1574
1575 if (method_len == NTR_MAX_SELECTION_METHOD_LEN) {
1576 OFPPROP_LOG(&rl, false,
1577 "ntr selection method is not null terminated");
1578 return OFPERR_OFPBPC_BAD_VALUE;
1579 }
1580
1581 if (strcmp("hash", prop->selection_method)
1582 && strcmp("dp_hash", prop->selection_method)) {
1583 OFPPROP_LOG(&rl, false,
1584 "ntr selection method '%s' is not supported",
1585 prop->selection_method);
1586 return OFPERR_OFPBPC_BAD_VALUE;
1587 }
1588 /* 'method_len' is now non-zero. */
1589
1590 strcpy(gp->selection_method, prop->selection_method);
1591 gp->selection_method_param = ntohll(prop->selection_method_param);
1592
1593 ofpbuf_pull(payload, sizeof *prop);
1594
1595 fields_len = ntohs(prop->length) - sizeof *prop;
1596 if (fields_len && strcmp("hash", gp->selection_method)) {
1597 OFPPROP_LOG(&rl, false, "ntr selection method %s "
1598 "does not support fields", gp->selection_method);
1599 return OFPERR_OFPBPC_BAD_VALUE;
1600 }
1601
1602 if (fields_len > 0) {
1603 error = oxm_pull_field_array(payload->data, fields_len,
1604 &gp->fields);
1605 if (error) {
1606 OFPPROP_LOG(&rl, false,
1607 "ntr selection method fields are invalid");
1608 return error;
1609 }
1610 } else {
1611 /* Selection_method "hash: w/o fields means default hash method. */
1612 gp->fields.values_size = 0;
1613 }
1614
1615 return 0;
1616 }
1617
1618 static enum ofperr
1619 parse_ofp15_group_properties(struct ofpbuf *msg,
1620 enum ofp11_group_type group_type,
1621 enum ofp15_group_mod_command group_cmd,
1622 struct ofputil_group_props *gp,
1623 size_t properties_len)
1624 {
1625 struct ofpbuf properties = ofpbuf_const_initializer(
1626 ofpbuf_pull(msg, properties_len), properties_len);
1627 while (properties.size > 0) {
1628 struct ofpbuf payload;
1629 enum ofperr error;
1630 uint64_t type;
1631
1632 error = ofpprop_pull(&properties, &payload, &type);
1633 if (error) {
1634 return error;
1635 }
1636
1637 switch (type) {
1638 case OFPPROP_EXP(NTR_VENDOR_ID, NTRT_SELECTION_METHOD):
1639 case OFPPROP_EXP(NTR_COMPAT_VENDOR_ID, NTRT_SELECTION_METHOD):
1640 error = parse_group_prop_ntr_selection_method(&payload, group_type,
1641 group_cmd, gp);
1642 break;
1643
1644 default:
1645 error = OFPPROP_UNKNOWN(false, "group", type);
1646 break;
1647 }
1648
1649 if (error) {
1650 return error;
1651 }
1652 }
1653
1654 return 0;
1655 }
1656
1657 static int
1658 ofputil_decode_ofp11_group_desc_reply(struct ofputil_group_desc *gd,
1659 struct ofpbuf *msg,
1660 enum ofp_version version)
1661 {
1662 struct ofp11_group_desc_stats *ogds;
1663 size_t length;
1664
1665 if (!msg->header) {
1666 ofpraw_pull_assert(msg);
1667 }
1668
1669 if (!msg->size) {
1670 return EOF;
1671 }
1672
1673 ogds = ofpbuf_try_pull(msg, sizeof *ogds);
1674 if (!ogds) {
1675 VLOG_WARN_RL(&rl, "OFPST11_GROUP_DESC reply has %"PRIu32" "
1676 "leftover bytes at end", msg->size);
1677 return OFPERR_OFPBRC_BAD_LEN;
1678 }
1679 gd->type = ogds->type;
1680 gd->group_id = ntohl(ogds->group_id);
1681
1682 length = ntohs(ogds->length);
1683 if (length < sizeof *ogds || length - sizeof *ogds > msg->size) {
1684 VLOG_WARN_RL(&rl, "OFPST11_GROUP_DESC reply claims invalid "
1685 "length %"PRIuSIZE, length);
1686 return OFPERR_OFPBRC_BAD_LEN;
1687 }
1688
1689 return ofputil_pull_ofp11_buckets(msg, length - sizeof *ogds, version,
1690 &gd->buckets);
1691 }
1692
1693 static int
1694 ofputil_decode_ofp15_group_desc_reply(struct ofputil_group_desc *gd,
1695 struct ofpbuf *msg,
1696 enum ofp_version version)
1697 {
1698 struct ofp15_group_desc_stats *ogds;
1699 uint16_t length, bucket_list_len;
1700 int error;
1701
1702 if (!msg->header) {
1703 ofpraw_pull_assert(msg);
1704 }
1705
1706 if (!msg->size) {
1707 return EOF;
1708 }
1709
1710 ogds = ofpbuf_try_pull(msg, sizeof *ogds);
1711 if (!ogds) {
1712 VLOG_WARN_RL(&rl, "OFPST11_GROUP_DESC reply has %"PRIu32" "
1713 "leftover bytes at end", msg->size);
1714 return OFPERR_OFPBRC_BAD_LEN;
1715 }
1716 gd->type = ogds->type;
1717 gd->group_id = ntohl(ogds->group_id);
1718
1719 length = ntohs(ogds->length);
1720 if (length < sizeof *ogds || length - sizeof *ogds > msg->size) {
1721 VLOG_WARN_RL(&rl, "OFPST11_GROUP_DESC reply claims invalid "
1722 "length %u", length);
1723 return OFPERR_OFPBRC_BAD_LEN;
1724 }
1725
1726 bucket_list_len = ntohs(ogds->bucket_list_len);
1727 if (length < bucket_list_len + sizeof *ogds) {
1728 VLOG_WARN_RL(&rl, "OFPST11_GROUP_DESC reply claims invalid "
1729 "bucket list length %u", bucket_list_len);
1730 return OFPERR_OFPBRC_BAD_LEN;
1731 }
1732 error = ofputil_pull_ofp15_buckets(msg, bucket_list_len, version, gd->type,
1733 &gd->buckets);
1734 if (error) {
1735 return error;
1736 }
1737
1738 /* By definition group desc messages don't have a group mod command.
1739 * However, parse_group_prop_ntr_selection_method() checks to make sure
1740 * that the command is OFPGC15_ADD or OFPGC15_DELETE to guard
1741 * against group mod messages with other commands supplying
1742 * a NTR selection method group experimenter property.
1743 * Such properties are valid for group desc replies so
1744 * claim that the group mod command is OFPGC15_ADD to
1745 * satisfy the check in parse_group_prop_ntr_selection_method() */
1746 error = parse_ofp15_group_properties(
1747 msg, gd->type, OFPGC15_ADD, &gd->props,
1748 length - sizeof *ogds - bucket_list_len);
1749 if (error) {
1750 ofputil_uninit_group_desc(gd);
1751 }
1752 return error;
1753 }
1754
1755 /* Converts a group description reply in 'msg' into an abstract
1756 * ofputil_group_desc in 'gd'.
1757 *
1758 * Multiple group description replies can be packed into a single OpenFlow
1759 * message. Calling this function multiple times for a single 'msg' iterates
1760 * through the replies. The caller must initially leave 'msg''s layer pointers
1761 * null and not modify them between calls.
1762 *
1763 * Returns 0 if successful, EOF if no replies were left in this 'msg',
1764 * otherwise a positive errno value. */
1765 int
1766 ofputil_decode_group_desc_reply(struct ofputil_group_desc *gd,
1767 struct ofpbuf *msg, enum ofp_version version)
1768 {
1769 ofputil_init_group_properties(&gd->props);
1770
1771 switch (version)
1772 {
1773 case OFP11_VERSION:
1774 case OFP12_VERSION:
1775 case OFP13_VERSION:
1776 case OFP14_VERSION:
1777 return ofputil_decode_ofp11_group_desc_reply(gd, msg, version);
1778
1779 case OFP10_VERSION:
1780 case OFP15_VERSION:
1781 return ofputil_decode_ofp15_group_desc_reply(gd, msg, version);
1782
1783 default:
1784 OVS_NOT_REACHED();
1785 }
1786 }
1787
1788 static void
1789 ofp_print_bucket_id(struct ds *s, const char *label, uint32_t bucket_id,
1790 enum ofp_version ofp_version)
1791 {
1792 if (ofp_version > OFP10_VERSION && ofp_version < OFP15_VERSION) {
1793 return;
1794 }
1795
1796 ds_put_cstr(s, label);
1797
1798 switch (bucket_id) {
1799 case OFPG15_BUCKET_FIRST:
1800 ds_put_cstr(s, "first");
1801 break;
1802 case OFPG15_BUCKET_LAST:
1803 ds_put_cstr(s, "last");
1804 break;
1805 case OFPG15_BUCKET_ALL:
1806 ds_put_cstr(s, "all");
1807 break;
1808 default:
1809 ds_put_format(s, "%"PRIu32, bucket_id);
1810 break;
1811 }
1812
1813 ds_put_char(s, ',');
1814 }
1815
1816 static void
1817 ofp_print_group(struct ds *s, uint32_t group_id, uint8_t type,
1818 const struct ovs_list *p_buckets,
1819 const struct ofputil_group_props *props,
1820 enum ofp_version ofp_version, bool suppress_type,
1821 const struct ofputil_port_map *port_map,
1822 const struct ofputil_table_map *table_map)
1823 {
1824 struct ofputil_bucket *bucket;
1825
1826 ds_put_format(s, "group_id=%"PRIu32, group_id);
1827
1828 if (!suppress_type) {
1829 static const char *type_str[] = { "all", "select", "indirect",
1830 "ff", "unknown" };
1831 ds_put_format(s, ",type=%s", type_str[type > 4 ? 4 : type]);
1832 }
1833
1834 if (props->selection_method[0]) {
1835 ds_put_format(s, ",selection_method=%s", props->selection_method);
1836 if (props->selection_method_param) {
1837 ds_put_format(s, ",selection_method_param=%"PRIu64,
1838 props->selection_method_param);
1839 }
1840
1841 size_t n = bitmap_count1(props->fields.used.bm, MFF_N_IDS);
1842 if (n == 1) {
1843 ds_put_cstr(s, ",fields=");
1844 oxm_format_field_array(s, &props->fields);
1845 } else if (n > 1) {
1846 ds_put_cstr(s, ",fields(");
1847 oxm_format_field_array(s, &props->fields);
1848 ds_put_char(s, ')');
1849 }
1850 }
1851
1852 if (!p_buckets) {
1853 return;
1854 }
1855
1856 ds_put_char(s, ',');
1857
1858 LIST_FOR_EACH (bucket, list_node, p_buckets) {
1859 ds_put_cstr(s, "bucket=");
1860
1861 ofp_print_bucket_id(s, "bucket_id:", bucket->bucket_id, ofp_version);
1862 if (bucket->weight != (type == OFPGT11_SELECT ? 1 : 0)) {
1863 ds_put_format(s, "weight:%"PRIu16",", bucket->weight);
1864 }
1865 if (bucket->watch_port != OFPP_NONE) {
1866 ds_put_cstr(s, "watch_port:");
1867 ofputil_format_port(bucket->watch_port, port_map, s);
1868 ds_put_char(s, ',');
1869 }
1870 if (bucket->watch_group != OFPG_ANY) {
1871 ds_put_format(s, "watch_group:%"PRIu32",", bucket->watch_group);
1872 }
1873
1874 ds_put_cstr(s, "actions=");
1875 struct ofpact_format_params fp = {
1876 .port_map = port_map,
1877 .table_map = table_map,
1878 .s = s,
1879 };
1880 ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, &fp);
1881 ds_put_char(s, ',');
1882 }
1883
1884 ds_chomp(s, ',');
1885 }
1886
1887 enum ofperr
1888 ofputil_group_desc_format(struct ds *s, const struct ofp_header *oh,
1889 const struct ofputil_port_map *port_map,
1890 const struct ofputil_table_map *table_map)
1891 {
1892 struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
1893 for (;;) {
1894 struct ofputil_group_desc gd;
1895 int retval;
1896
1897 retval = ofputil_decode_group_desc_reply(&gd, &b, oh->version);
1898 if (retval) {
1899 return retval != EOF ? retval : 0;
1900 }
1901
1902 ds_put_char(s, '\n');
1903 ds_put_char(s, ' ');
1904 ofp_print_group(s, gd.group_id, gd.type, &gd.buckets, &gd.props,
1905 oh->version, false, port_map, table_map);
1906 ofputil_uninit_group_desc(&gd);
1907 }
1908 }
1909
1910 void
1911 ofputil_uninit_group_mod(struct ofputil_group_mod *gm)
1912 {
1913 ofputil_bucket_list_destroy(&gm->buckets);
1914 ofputil_group_properties_destroy(&gm->props);
1915 }
1916
1917 static void
1918 bad_group_cmd(enum ofp15_group_mod_command cmd)
1919 {
1920 const char *opt_version;
1921 const char *version;
1922 const char *cmd_str;
1923
1924 switch (cmd) {
1925 case OFPGC15_ADD:
1926 case OFPGC15_MODIFY:
1927 case OFPGC15_ADD_OR_MOD:
1928 case OFPGC15_DELETE:
1929 version = "1.1";
1930 opt_version = "11";
1931 break;
1932
1933 case OFPGC15_INSERT_BUCKET:
1934 case OFPGC15_REMOVE_BUCKET:
1935 version = "1.5";
1936 opt_version = "15";
1937 break;
1938
1939 default:
1940 OVS_NOT_REACHED();
1941 }
1942
1943 switch (cmd) {
1944 case OFPGC15_ADD:
1945 cmd_str = "add-group";
1946 break;
1947
1948 case OFPGC15_MODIFY:
1949 case OFPGC15_ADD_OR_MOD:
1950 cmd_str = "mod-group";
1951 break;
1952
1953 case OFPGC15_DELETE:
1954 cmd_str = "del-group";
1955 break;
1956
1957 case OFPGC15_INSERT_BUCKET:
1958 cmd_str = "insert-bucket";
1959 break;
1960
1961 case OFPGC15_REMOVE_BUCKET:
1962 cmd_str = "remove-bucket";
1963 break;
1964
1965 default:
1966 OVS_NOT_REACHED();
1967 }
1968
1969 ovs_fatal(0, "%s needs OpenFlow %s or later (\'-O OpenFlow%s\')",
1970 cmd_str, version, opt_version);
1971
1972 }
1973
1974 static struct ofpbuf *
1975 ofputil_encode_ofp11_group_mod(enum ofp_version ofp_version,
1976 const struct ofputil_group_mod *gm,
1977 const struct ovs_list *new_buckets,
1978 int group_existed)
1979 {
1980 struct ofpbuf *b;
1981 struct ofp11_group_mod *ogm;
1982 size_t start_ogm;
1983 struct ofputil_bucket *bucket;
1984
1985 b = ofpraw_alloc(OFPRAW_OFPT11_GROUP_MOD, ofp_version, 0);
1986 start_ogm = b->size;
1987 ofpbuf_put_zeros(b, sizeof *ogm);
1988
1989 uint16_t command = gm->command;
1990 const struct ovs_list *buckets = &gm->buckets;
1991 switch (gm->command) {
1992 case OFPGC15_INSERT_BUCKET:
1993 case OFPGC15_REMOVE_BUCKET:
1994 if (!new_buckets) {
1995 bad_group_cmd(gm->command);
1996 }
1997 command = OFPGC11_MODIFY;
1998 buckets = new_buckets;
1999 break;
2000
2001 case OFPGC11_ADD_OR_MOD:
2002 if (group_existed >= 0) {
2003 command = group_existed ? OFPGC11_MODIFY : OFPGC11_ADD;
2004 }
2005 break;
2006
2007 default:
2008 break;
2009 }
2010 LIST_FOR_EACH (bucket, list_node, buckets) {
2011 ofputil_put_ofp11_bucket(bucket, b, ofp_version);
2012 }
2013 ogm = ofpbuf_at_assert(b, start_ogm, sizeof *ogm);
2014 ogm->command = htons(command);
2015 ogm->type = gm->type;
2016 ogm->group_id = htonl(gm->group_id);
2017
2018 ofpmsg_update_length(b);
2019
2020 return b;
2021 }
2022
2023 static struct ofpbuf *
2024 ofputil_encode_ofp15_group_mod(enum ofp_version ofp_version,
2025 const struct ofputil_group_mod *gm,
2026 int group_existed)
2027 {
2028 struct ofpbuf *b;
2029 struct ofp15_group_mod *ogm;
2030 size_t start_ogm;
2031 struct ofputil_bucket *bucket;
2032 struct id_pool *bucket_ids = NULL;
2033
2034 b = ofpraw_alloc((ofp_version == OFP10_VERSION
2035 ? OFPRAW_NXT_GROUP_MOD
2036 : OFPRAW_OFPT15_GROUP_MOD), ofp_version, 0);
2037 start_ogm = b->size;
2038 ofpbuf_put_zeros(b, sizeof *ogm);
2039
2040 LIST_FOR_EACH (bucket, list_node, &gm->buckets) {
2041 uint32_t bucket_id;
2042
2043 /* Generate a bucket id if none was supplied */
2044 if (bucket->bucket_id > OFPG15_BUCKET_MAX) {
2045 if (!bucket_ids) {
2046 const struct ofputil_bucket *bkt;
2047
2048 bucket_ids = id_pool_create(0, OFPG15_BUCKET_MAX + 1);
2049
2050 /* Mark all bucket_ids that are present in gm
2051 * as used in the pool. */
2052 LIST_FOR_EACH_REVERSE (bkt, list_node, &gm->buckets) {
2053 if (bkt == bucket) {
2054 break;
2055 }
2056 if (bkt->bucket_id <= OFPG15_BUCKET_MAX) {
2057 id_pool_add(bucket_ids, bkt->bucket_id);
2058 }
2059 }
2060 }
2061
2062 if (!id_pool_alloc_id(bucket_ids, &bucket_id)) {
2063 OVS_NOT_REACHED();
2064 }
2065 } else {
2066 bucket_id = bucket->bucket_id;
2067 }
2068
2069 ofputil_put_ofp15_bucket(bucket, bucket_id, gm->type, b, ofp_version);
2070 }
2071 ogm = ofpbuf_at_assert(b, start_ogm, sizeof *ogm);
2072 ogm->command = htons(gm->command != OFPGC11_ADD_OR_MOD || group_existed < 0
2073 ? gm->command
2074 : group_existed ? OFPGC11_MODIFY : OFPGC11_ADD);
2075 ogm->type = gm->type;
2076 ogm->group_id = htonl(gm->group_id);
2077 ogm->command_bucket_id = htonl(gm->command_bucket_id);
2078 ogm->bucket_array_len = htons(b->size - start_ogm - sizeof *ogm);
2079
2080 /* Add group properties */
2081 if (gm->props.selection_method[0]) {
2082 ofputil_put_group_prop_ntr_selection_method(ofp_version, &gm->props, b);
2083 }
2084
2085 id_pool_destroy(bucket_ids);
2086 ofpmsg_update_length(b);
2087 return b;
2088 }
2089
2090 /* Converts abstract group mod 'gm' into a message for OpenFlow version
2091 * 'ofp_version' and returns the message.
2092 *
2093 * If 'new_buckets' is nonnull, it should point to the full set of new buckets
2094 * that resulted from a OFPGC15_INSERT_BUCKET or OFPGC15_REMOVE_BUCKET command.
2095 * It is needed to translate such group_mods into OpenFlow 1.1-1.4
2096 * OFPGC11_MODIFY. If it is null but needed for translation, then encoding the
2097 * group_mod will print an error on stderr and exit().
2098 *
2099 * If 'group_existed' is nonnegative, it should specify whether the group
2100 * existed before the command was executed. If it is nonnegative, then it is
2101 * used to translate OVS's nonstandard OFPGC11_ADD_OR_MOD into a standard
2102 * command. If it is negative, then OFPGC11_ADD_OR_MOD will be left as is. */
2103 struct ofpbuf *
2104 ofputil_encode_group_mod(enum ofp_version ofp_version,
2105 const struct ofputil_group_mod *gm,
2106 const struct ovs_list *new_buckets,
2107 int group_existed)
2108 {
2109
2110 switch (ofp_version) {
2111 case OFP11_VERSION:
2112 case OFP12_VERSION:
2113 case OFP13_VERSION:
2114 case OFP14_VERSION:
2115 return ofputil_encode_ofp11_group_mod(ofp_version, gm,
2116 new_buckets, group_existed);
2117
2118 case OFP10_VERSION:
2119 case OFP15_VERSION:
2120 return ofputil_encode_ofp15_group_mod(ofp_version, gm, group_existed);
2121
2122 default:
2123 OVS_NOT_REACHED();
2124 }
2125 }
2126
2127 static enum ofperr
2128 ofputil_pull_ofp11_group_mod(struct ofpbuf *msg, enum ofp_version ofp_version,
2129 struct ofputil_group_mod *gm)
2130 {
2131 const struct ofp11_group_mod *ogm;
2132 enum ofperr error;
2133
2134 ogm = ofpbuf_pull(msg, sizeof *ogm);
2135 gm->command = ntohs(ogm->command);
2136 gm->type = ogm->type;
2137 gm->group_id = ntohl(ogm->group_id);
2138 gm->command_bucket_id = OFPG15_BUCKET_ALL;
2139
2140 error = ofputil_pull_ofp11_buckets(msg, msg->size, ofp_version,
2141 &gm->buckets);
2142
2143 /* OF1.3.5+ prescribes an error when an OFPGC_DELETE includes buckets. */
2144 if (!error
2145 && ofp_version >= OFP13_VERSION
2146 && gm->command == OFPGC11_DELETE
2147 && !ovs_list_is_empty(&gm->buckets)) {
2148 error = OFPERR_OFPGMFC_INVALID_GROUP;
2149 ofputil_bucket_list_destroy(&gm->buckets);
2150 }
2151
2152 return error;
2153 }
2154
2155 static enum ofperr
2156 ofputil_pull_ofp15_group_mod(struct ofpbuf *msg, enum ofp_version ofp_version,
2157 struct ofputil_group_mod *gm)
2158 {
2159 const struct ofp15_group_mod *ogm;
2160 uint16_t bucket_list_len;
2161 enum ofperr error = OFPERR_OFPGMFC_BAD_BUCKET;
2162
2163 ogm = ofpbuf_pull(msg, sizeof *ogm);
2164 gm->command = ntohs(ogm->command);
2165 gm->type = ogm->type;
2166 gm->group_id = ntohl(ogm->group_id);
2167
2168 gm->command_bucket_id = ntohl(ogm->command_bucket_id);
2169 switch (gm->command) {
2170 case OFPGC15_REMOVE_BUCKET:
2171 if (gm->command_bucket_id == OFPG15_BUCKET_ALL) {
2172 error = 0;
2173 }
2174 /* Fall through */
2175 case OFPGC15_INSERT_BUCKET:
2176 if (gm->command_bucket_id <= OFPG15_BUCKET_MAX ||
2177 gm->command_bucket_id == OFPG15_BUCKET_FIRST
2178 || gm->command_bucket_id == OFPG15_BUCKET_LAST) {
2179 error = 0;
2180 }
2181 break;
2182
2183 case OFPGC11_ADD:
2184 case OFPGC11_MODIFY:
2185 case OFPGC11_ADD_OR_MOD:
2186 case OFPGC11_DELETE:
2187 default:
2188 if (gm->command_bucket_id == OFPG15_BUCKET_ALL) {
2189 error = 0;
2190 }
2191 break;
2192 }
2193 if (error) {
2194 VLOG_WARN_RL(&rl,
2195 "group command bucket id (%u) is out of range",
2196 gm->command_bucket_id);
2197 return OFPERR_OFPGMFC_BAD_BUCKET;
2198 }
2199
2200 bucket_list_len = ntohs(ogm->bucket_array_len);
2201 if (bucket_list_len > msg->size) {
2202 return OFPERR_OFPBRC_BAD_LEN;
2203 }
2204 error = ofputil_pull_ofp15_buckets(msg, bucket_list_len, ofp_version,
2205 gm->type, &gm->buckets);
2206 if (error) {
2207 return error;
2208 }
2209
2210 error = parse_ofp15_group_properties(msg, gm->type, gm->command,
2211 &gm->props, msg->size);
2212 if (error) {
2213 ofputil_uninit_group_mod(gm);
2214 }
2215 return error;
2216 }
2217
2218 static enum ofperr
2219 ofputil_check_group_mod(const struct ofputil_group_mod *gm)
2220 {
2221 switch (gm->type) {
2222 case OFPGT11_INDIRECT:
2223 if (gm->command != OFPGC11_DELETE
2224 && !ovs_list_is_singleton(&gm->buckets) ) {
2225 return OFPERR_OFPGMFC_INVALID_GROUP;
2226 }
2227 break;
2228 case OFPGT11_ALL:
2229 case OFPGT11_SELECT:
2230 case OFPGT11_FF:
2231 break;
2232 default:
2233 return OFPERR_OFPGMFC_BAD_TYPE;
2234 }
2235
2236 switch (gm->command) {
2237 case OFPGC11_ADD:
2238 case OFPGC11_MODIFY:
2239 case OFPGC11_ADD_OR_MOD:
2240 case OFPGC11_DELETE:
2241 case OFPGC15_INSERT_BUCKET:
2242 break;
2243 case OFPGC15_REMOVE_BUCKET:
2244 if (!ovs_list_is_empty(&gm->buckets)) {
2245 return OFPERR_OFPGMFC_BAD_BUCKET;
2246 }
2247 break;
2248 default:
2249 return OFPERR_OFPGMFC_BAD_COMMAND;
2250 }
2251
2252 struct ofputil_bucket *bucket;
2253 LIST_FOR_EACH (bucket, list_node, &gm->buckets) {
2254 if (bucket->weight && gm->type != OFPGT11_SELECT
2255 && gm->command != OFPGC15_INSERT_BUCKET) {
2256 return OFPERR_OFPGMFC_INVALID_GROUP;
2257 }
2258
2259 switch (gm->type) {
2260 case OFPGT11_ALL:
2261 case OFPGT11_INDIRECT:
2262 if (ofputil_bucket_has_liveness(bucket)) {
2263 return OFPERR_OFPGMFC_WATCH_UNSUPPORTED;
2264 }
2265 break;
2266 case OFPGT11_SELECT:
2267 break;
2268 case OFPGT11_FF:
2269 if (!ofputil_bucket_has_liveness(bucket)) {
2270 return OFPERR_OFPGMFC_INVALID_GROUP;
2271 }
2272 break;
2273 default:
2274 /* Returning BAD TYPE to be consistent
2275 * though gm->type has been checked already. */
2276 return OFPERR_OFPGMFC_BAD_TYPE;
2277 }
2278 }
2279
2280 return 0;
2281 }
2282
2283 /* Converts OpenFlow group mod message 'oh' into an abstract group mod in
2284 * 'gm'. Returns 0 if successful, otherwise an OpenFlow error code. */
2285 enum ofperr
2286 ofputil_decode_group_mod(const struct ofp_header *oh,
2287 struct ofputil_group_mod *gm)
2288 {
2289 ofputil_init_group_properties(&gm->props);
2290
2291 enum ofp_version ofp_version = oh->version;
2292 struct ofpbuf msg = ofpbuf_const_initializer(oh, ntohs(oh->length));
2293 ofpraw_pull_assert(&msg);
2294
2295 enum ofperr err;
2296 switch (ofp_version) {
2297 case OFP11_VERSION:
2298 case OFP12_VERSION:
2299 case OFP13_VERSION:
2300 case OFP14_VERSION:
2301 err = ofputil_pull_ofp11_group_mod(&msg, ofp_version, gm);
2302 break;
2303
2304 case OFP10_VERSION:
2305 case OFP15_VERSION:
2306 err = ofputil_pull_ofp15_group_mod(&msg, ofp_version, gm);
2307 break;
2308
2309 default:
2310 OVS_NOT_REACHED();
2311 }
2312 if (err) {
2313 return err;
2314 }
2315
2316 err = ofputil_check_group_mod(gm);
2317 if (err) {
2318 ofputil_uninit_group_mod(gm);
2319 }
2320 return err;
2321 }
2322
2323 void
2324 ofputil_group_mod_format__(struct ds *s, enum ofp_version ofp_version,
2325 const struct ofputil_group_mod *gm,
2326 const struct ofputil_port_map *port_map,
2327 const struct ofputil_table_map *table_map)
2328 {
2329 bool bucket_command = false;
2330
2331 ds_put_char(s, '\n');
2332
2333 ds_put_char(s, ' ');
2334 switch (gm->command) {
2335 case OFPGC11_ADD:
2336 ds_put_cstr(s, "ADD");
2337 break;
2338
2339 case OFPGC11_MODIFY:
2340 ds_put_cstr(s, "MOD");
2341 break;
2342
2343 case OFPGC11_ADD_OR_MOD:
2344 ds_put_cstr(s, "ADD_OR_MOD");
2345 break;
2346
2347 case OFPGC11_DELETE:
2348 ds_put_cstr(s, "DEL");
2349 break;
2350
2351 case OFPGC15_INSERT_BUCKET:
2352 ds_put_cstr(s, "INSERT_BUCKET");
2353 bucket_command = true;
2354 break;
2355
2356 case OFPGC15_REMOVE_BUCKET:
2357 ds_put_cstr(s, "REMOVE_BUCKET");
2358 bucket_command = true;
2359 break;
2360
2361 default:
2362 ds_put_format(s, "cmd:%"PRIu16"", gm->command);
2363 }
2364 ds_put_char(s, ' ');
2365
2366 if (bucket_command) {
2367 ofp_print_bucket_id(s, "command_bucket_id:",
2368 gm->command_bucket_id, ofp_version);
2369 }
2370
2371 ofp_print_group(s, gm->group_id, gm->type, &gm->buckets, &gm->props,
2372 ofp_version, bucket_command, port_map, table_map);
2373 }
2374
2375 enum ofperr
2376 ofputil_group_mod_format(struct ds *s, const struct ofp_header *oh,
2377 const struct ofputil_port_map *port_map,
2378 const struct ofputil_table_map *table_map)
2379 {
2380 struct ofputil_group_mod gm;
2381 int error;
2382
2383 error = ofputil_decode_group_mod(oh, &gm);
2384 if (error) {
2385 return error;
2386 }
2387 ofputil_group_mod_format__(s, oh->version, &gm, port_map, table_map);
2388 ofputil_uninit_group_mod(&gm);
2389 return 0;
2390 }