2 * Copyright (c) 2008-2017, 2019 Nicira, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "openvswitch/ofp-group.h"
20 #include "byte-order.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"
34 VLOG_DEFINE_THIS_MODULE(ofp_group
);
36 static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(1, 5);
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").
42 * Returns true if successful, false if 's' is not a valid OpenFlow group ID or
45 ofputil_group_from_string(const char *s
, uint32_t *group_idp
)
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 "
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". */
65 ofputil_format_group(uint32_t group_id
, struct ds
*s
)
67 char name
[MAX_GROUP_NAME_LEN
];
69 ofputil_group_to_string(group_id
, name
, sizeof name
);
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". */
79 ofputil_group_to_string(uint32_t group_id
,
80 char namebuf
[MAX_GROUP_NAME_LEN
+ 1], size_t bufsize
)
84 ovs_strlcpy(namebuf
, "ALL", bufsize
);
88 ovs_strlcpy(namebuf
, "ANY", bufsize
);
92 snprintf(namebuf
, bufsize
, "%"PRIu32
, group_id
);
97 /* Frees all of the "struct ofputil_bucket"s in the 'buckets' list. */
99 ofputil_bucket_list_destroy(struct ovs_list
*buckets
)
101 struct ofputil_bucket
*bucket
;
103 LIST_FOR_EACH_POP (bucket
, list_node
, buckets
) {
104 free(bucket
->ofpacts
);
109 /* Clones 'bucket' and its ofpacts data */
110 static struct ofputil_bucket
*
111 ofputil_bucket_clone_data(const struct ofputil_bucket
*bucket
)
113 struct ofputil_bucket
*new;
115 new = xmemdup(bucket
, sizeof *bucket
);
116 new->ofpacts
= xmemdup(bucket
->ofpacts
, bucket
->ofpacts_len
);
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'. */
128 ofputil_bucket_clone_list(struct ovs_list
*dest
, const struct ovs_list
*src
,
129 const struct ofputil_bucket
*skip
)
131 struct ofputil_bucket
*bucket
;
133 LIST_FOR_EACH (bucket
, list_node
, src
) {
134 struct ofputil_bucket
*new_bucket
;
136 if (bucket
== skip
) {
140 new_bucket
= ofputil_bucket_clone_data(bucket
);
141 ovs_list_push_back(dest
, &new_bucket
->list_node
);
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
)
150 struct ofputil_bucket
*bucket
;
152 if (bucket_id
> OFPG15_BUCKET_MAX
) {
156 LIST_FOR_EACH (bucket
, list_node
, buckets
) {
157 if (bucket
->bucket_id
== bucket_id
) {
165 /* Returns true if more than one bucket in the list 'buckets'
166 * have the same bucket id. Returns false otherwise. */
168 ofputil_bucket_check_duplicate_id(const struct ovs_list
*buckets
)
170 struct ofputil_bucket
*i
, *j
;
172 LIST_FOR_EACH (i
, list_node
, buckets
) {
173 LIST_FOR_EACH_REVERSE (j
, list_node
, buckets
) {
177 if (i
->bucket_id
== j
->bucket_id
) {
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
)
191 static struct ofputil_bucket
*bucket
;
193 ASSIGN_CONTAINER(bucket
, ovs_list_front(buckets
), list_node
);
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
)
203 static struct ofputil_bucket
*bucket
;
205 ASSIGN_CONTAINER(bucket
, ovs_list_back(buckets
), list_node
);
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
214 * Group statistics include packet and byte counts for each group. */
216 ofputil_encode_group_stats_request(enum ofp_version ofp_version
,
219 struct ofpbuf
*msg
= ofpraw_alloc((ofp_version
== OFP10_VERSION
220 ? OFPRAW_NXST_GROUP_REQUEST
221 : OFPRAW_OFPST11_GROUP_REQUEST
),
223 struct ofp11_group_stats_request
*req
= ofpbuf_put_zeros(msg
, sizeof *req
);
224 req
->group_id
= htonl(group_id
);
230 ofputil_uninit_group_desc(struct ofputil_group_desc
*gd
)
232 ofputil_bucket_list_destroy(&gd
->buckets
);
233 ofputil_group_properties_destroy(&gd
->props
);
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
240 ofputil_decode_group_desc_request(const struct ofp_header
*oh
)
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
) {
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
);
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
260 * Group descriptions include the bucket and action configuration for each
263 ofputil_encode_group_desc_request(enum ofp_version ofp_version
,
266 struct ofpbuf
*request
;
268 switch (ofp_version
) {
273 request
= ofpraw_alloc(OFPRAW_OFPST11_GROUP_DESC_REQUEST
,
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
),
283 req
= ofpbuf_put_zeros(request
, sizeof *req
);
284 req
->group_id
= htonl(group_id
);
296 ofputil_group_desc_request_format(struct ds
*string
,
297 const struct ofp_header
*oh
)
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
);
307 ofputil_group_bucket_counters_to_ofp11(const struct ofputil_group_stats
*gs
,
308 struct ofp11_bucket_counter bucket_cnts
[])
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
);
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
[])
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
);
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
[])
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
);
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(). */
347 ofputil_append_group_stats(struct ovs_list
*replies
,
348 const struct ofputil_group_stats
*gs
)
350 size_t bucket_counter_size
;
351 struct ofp11_bucket_counter
*bucket_counters
;
354 bucket_counter_size
= gs
->n_buckets
* sizeof(struct ofp11_bucket_counter
);
356 switch (ofpmp_version(replies
)) {
359 struct ofp11_group_stats
*gs11
;
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
);
371 case OFP15_VERSION
: {
372 struct ofp13_group_stats
*gs13
;
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
);
386 /* Returns an OpenFlow group features request for OpenFlow version
389 ofputil_encode_group_features_request(enum ofp_version ofp_version
)
391 return ofpraw_alloc((ofp_version
< OFP12_VERSION
392 ? OFPRAW_NXST_GROUP_FEATURES_REQUEST
393 : OFPRAW_OFPST12_GROUP_FEATURES_REQUEST
),
397 /* Returns a OpenFlow message that encodes 'features' properly as a reply to
398 * group features request 'request'. */
400 ofputil_encode_group_features_reply(
401 const struct ofputil_group_features
*features
,
402 const struct ofp_header
*request
)
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
],
418 /* Decodes group features reply 'oh' into 'features'. */
420 ofputil_decode_group_features_reply(const struct ofp_header
*oh
,
421 struct ofputil_group_features
*features
)
423 const struct ofp12_group_features_stats
*ogf
= ofpmsg_body(oh
);
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
);
436 group_type_to_string(enum ofp11_group_type 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();
448 ofputil_group_features_format(struct ds
*string
, const struct ofp_header
*oh
)
450 struct ofputil_group_features features
;
453 ofputil_decode_group_features_reply(oh
, &features
);
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
);
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');
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. */
478 ofputil_decode_group_stats_request(const struct ofp_header
*request
,
481 const struct ofp11_group_stats_request
*gsr11
= ofpmsg_body(request
);
482 *group_id
= ntohl(gsr11
->group_id
);
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.
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.
495 * Returns 0 if successful, EOF if no replies were left in this 'msg',
496 * otherwise a positive errno value. */
498 ofputil_decode_group_stats_reply(struct ofpbuf
*msg
,
499 struct ofputil_group_stats
*gs
)
501 struct ofp11_bucket_counter
*obc
;
502 struct ofp11_group_stats
*ogs11
;
509 gs
->bucket_stats
= NULL
;
510 error
= (msg
->header
? ofpraw_decode(&raw
, msg
->header
)
511 : ofpraw_pull(&raw
, msg
));
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
;
528 base_len
= sizeof *ogs13
;
529 ogs13
= ofpbuf_try_pull(msg
, sizeof *ogs13
);
532 gs
->duration_sec
= ntohl(ogs13
->duration_sec
);
533 gs
->duration_nsec
= ntohl(ogs13
->duration_nsec
);
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
;
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
;
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
);
558 gs
->n_buckets
= (length
- base_len
) / sizeof *obc
;
559 obc
= ofpbuf_try_pull(msg
, gs
->n_buckets
* sizeof *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
;
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
);
577 ofputil_group_stats_request_format(struct ds
*string
,
578 const struct ofp_header
*oh
)
583 error
= ofputil_decode_group_stats_request(oh
, &group_id
);
588 ds_put_cstr(string
, " group_id=");
589 ofputil_format_group(group_id
, string
);
594 ofputil_group_stats_format(struct ds
*s
, const struct ofp_header
*oh
)
596 struct ofpbuf b
= ofpbuf_const_initializer(oh
, ntohs(oh
->length
));
598 struct ofputil_group_stats gs
;
601 retval
= ofputil_decode_group_stats_reply(&b
, &gs
);
604 ds_put_cstr(s
, " ***parse error***");
610 ds_put_char(s
, '\n');
613 ds_put_format(s
, "group_id=%"PRIu32
",", gs
.group_id
);
615 if (gs
.duration_sec
!= UINT32_MAX
) {
616 ds_put_cstr(s
, "duration=");
617 ofp_print_duration(s
, gs
.duration_sec
, gs
.duration_nsec
);
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
);
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
);
632 free(gs
.bucket_stats
);
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
)
643 char *pos
, *key
, *value
;
644 struct ofpbuf ofpacts
;
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
;
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
);
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
);
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
,
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
);
683 ds_put_format(&actions
, "%s(%s),", key
, value
);
687 ds_destroy(&actions
);
692 if (!actions
.length
) {
693 return xstrdup("bucket must specify actions");
695 if (group_type
== OFPGT11_FF
&& !ofputil_bucket_has_liveness(bucket
)) {
696 return xstrdup("fast failover bucket requires watch_port or "
699 ds_chomp(&actions
, ',');
701 ofpbuf_init(&ofpacts
, 0);
702 struct ofpact_parse_params pp
= {
703 .port_map
= port_map
,
704 .table_map
= table_map
,
706 .usable_protocols
= usable_protocols
,
708 error
= ofpacts_parse_actions(ds_cstr(&actions
), &pp
);
709 ds_destroy(&actions
);
711 ofpbuf_uninit(&ofpacts
);
714 bucket
->ofpacts
= ofpacts
.data
;
715 bucket
->ofpacts_len
= ofpacts
.size
;
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
)
725 char *name
, *value_str
;
727 while (ofputil_parse_key_value(&s
, &name
, &value_str
)) {
728 const struct mf_field
*mf
= mf_from_name(name
);
732 union mf_value value
;
734 if (bitmap_is_set(fa
->used
.bm
, mf
->id
)) {
735 return xasprintf("%s: duplicate field", name
);
739 error
= mf_parse_value(mf
, value_str
, port_map
, &value
);
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
);
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
);
758 memset(&value
, 0xff, mf
->n_bytes
);
761 field_array_set(mf
->id
, &value
, fa
);
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
;
769 *usable_protocols
&= mf
->usable_protocols_bitwise
;
772 return xasprintf("%s: unknown field %s", s
, name
);
779 static char * OVS_WARN_UNUSED_RESULT
780 parse_ofp_group_mod_str__(struct ofputil_group_mod
*gm
, int command
,
782 const struct ofputil_port_map
*port_map
,
783 const struct ofputil_table_map
*table_map
,
784 enum ofputil_protocol
*usable_protocols
)
787 F_GROUP_TYPE
= 1 << 0,
789 F_COMMAND_BUCKET_ID
= 1 << 2,
790 F_COMMAND_BUCKET_ID_ALL
= 1 << 3,
792 bool had_type
= false;
793 bool had_command_bucket_id
= false;
794 struct ofputil_bucket
*bucket
;
797 *usable_protocols
= OFPUTIL_P_ANY
;
802 string
+= strspn(string
, " \t\r\n"); /* Skip white space. */
803 len
= strcspn(string
, ", \t\r\n"); /* Get length of the first token. */
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
;
819 command
= OFPGC11_ADD
;
826 fields
= F_GROUP_TYPE
| F_BUCKETS
;
834 fields
= F_GROUP_TYPE
| F_BUCKETS
;
837 case OFPGC11_ADD_OR_MOD
:
838 fields
= F_GROUP_TYPE
| F_BUCKETS
;
841 case OFPGC15_INSERT_BUCKET
:
842 fields
= F_BUCKETS
| F_COMMAND_BUCKET_ID
;
843 *usable_protocols
&= OFPUTIL_P_OF10_ANY
| OFPUTIL_P_OF15_UP
;
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
;
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
;
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=");
870 if (!(fields
& F_BUCKETS
)) {
871 error
= xstrdup("bucket is not needed");
877 /* Parse everything before the buckets. */
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");
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
;
893 error
= str_to_u32(value
, &gm
->command_bucket_id
);
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
);
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");
911 had_command_bucket_id
= true;
912 } else if (!strcmp(name
, "group_id")) {
913 if(!strcmp(value
, "all")) {
914 gm
->group_id
= OFPG_ALL
;
916 error
= str_to_u32(value
, &gm
->group_id
);
920 if (gm
->group_id
!= OFPG_ALL
&& gm
->group_id
> OFPG_MAX
) {
921 error
= xasprintf("invalid group id %"PRIu32
,
926 } else if (!strcmp(name
, "type")){
927 if (!(fields
& F_GROUP_TYPE
)) {
928 error
= xstrdup("type is not needed");
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
;
941 error
= xasprintf("invalid group type %s", value
);
945 } else if (!strcmp(name
, "selection_method")) {
946 if (!(fields
& F_GROUP_TYPE
)) {
947 error
= xstrdup("selection method is not needed");
950 if (strlen(value
) >= NTR_MAX_SELECTION_METHOD_LEN
) {
951 error
= xasprintf("selection method is longer than %u"
953 NTR_MAX_SELECTION_METHOD_LEN
- 1);
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");
965 error
= str_to_u64(value
, &gm
->props
.selection_method_param
);
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");
975 error
= parse_select_group_field(value
, port_map
,
981 *usable_protocols
&= OFPUTIL_P_OF10_ANY
| OFPUTIL_P_OF15_UP
;
983 error
= xasprintf("unknown keyword %s", name
);
987 if (gm
->group_id
== OFPG_ANY
) {
988 error
= xstrdup("must specify a group_id");
991 if (fields
& F_GROUP_TYPE
&& !had_type
) {
992 error
= xstrdup("must specify a type");
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\"");
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\"");
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");
1015 } else if (had_command_bucket_id
) {
1016 error
= xstrdup("command bucket id is not needed");
1020 /* Now parse the buckets, if any. */
1024 bkt_str
= strchr(bkt_str
+ 1, '=');
1026 error
= xstrdup("must specify bucket content");
1031 next_bkt_str
= strstr(bkt_str
, "bucket=");
1033 *next_bkt_str
= '\0';
1036 bucket
= xzalloc(sizeof(struct ofputil_bucket
));
1037 error
= parse_bucket_str(bucket
, bkt_str
, port_map
, table_map
,
1038 gm
->type
, usable_protocols
);
1043 ovs_list_push_back(&gm
->buckets
, &bucket
->list_node
);
1045 if (gm
->command
!= OFPGC15_INSERT_BUCKET
1046 && gm
->type
!= OFPGT11_SELECT
&& bucket
->weight
) {
1047 error
= xstrdup("Only select groups can have bucket weights.");
1051 bkt_str
= next_bkt_str
;
1053 if (gm
->type
== OFPGT11_INDIRECT
&& !ovs_list_is_short(&gm
->buckets
)) {
1054 error
= xstrdup("Indirect groups can have at most one bucket.");
1060 ofputil_uninit_group_mod(gm
);
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".
1068 char * OVS_WARN_UNUSED_RESULT
1069 parse_ofp_group_mod_str(struct ofputil_group_mod
*gm
, int command
,
1071 const struct ofputil_port_map
*port_map
,
1072 const struct ofputil_table_map
*table_map
,
1073 enum ofputil_protocol
*usable_protocols
)
1075 char *string
= xstrdup(str_
);
1076 char *error
= parse_ofp_group_mod_str__(gm
, command
, string
, port_map
,
1077 table_map
, usable_protocols
);
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".
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
,
1091 struct ofputil_group_mod
**gms
, size_t *n_gms
,
1092 enum ofputil_protocol
*usable_protocols
)
1094 size_t allocated_gms
;
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
));
1108 allocated_gms
= *n_gms
;
1111 *usable_protocols
= OFPUTIL_P_ANY
;
1112 while (!ds_get_preprocessed_line(&s
, stream
, &line_number
)) {
1113 enum ofputil_protocol usable
;
1116 if (*n_gms
>= allocated_gms
) {
1117 struct ofputil_group_mod
*new_gms
;
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
);
1126 error
= parse_ofp_group_mod_str(&(*gms
)[*n_gms
], command
, ds_cstr(&s
),
1127 port_map
, table_map
, &usable
);
1131 for (i
= 0; i
< *n_gms
; i
++) {
1132 ofputil_uninit_group_mod(&(*gms
)[i
]);
1139 if (stream
!= stdin
) {
1143 char *ret
= xasprintf("%s:%d: %s", file_name
, line_number
, error
);
1147 *usable_protocols
&= usable
;
1152 if (stream
!= stdin
) {
1159 ofputil_put_ofp11_bucket(const struct ofputil_bucket
*bucket
,
1160 struct ofpbuf
*openflow
, enum ofp_version ofp_version
)
1162 struct ofp11_bucket
*ob
;
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
);
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
)
1181 struct ofp15_bucket
*ob
;
1182 size_t start
, actions_start
, actions_len
;
1184 start
= openflow
->size
;
1185 ofpbuf_put_zeros(openflow
, sizeof *ob
);
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
;
1192 if (group_type
== OFPGT11_SELECT
|| bucket
->weight
) {
1193 ofpprop_put_u16(openflow
, OFPGBPT15_WEIGHT
, bucket
->weight
);
1195 if (bucket
->watch_port
!= OFPP_ANY
) {
1196 ofpprop_put_be32(openflow
, OFPGBPT15_WATCH_PORT
,
1197 ofputil_port_to_ofp11(bucket
->watch_port
));
1199 if (bucket
->watch_group
!= OFPG_ANY
) {
1200 ofpprop_put_u32(openflow
, OFPGBPT15_WATCH_GROUP
, bucket
->watch_group
);
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
);
1210 ofputil_put_group_prop_ntr_selection_method(enum ofp_version ofp_version
,
1211 const struct ofputil_group_props
*gp
,
1212 struct ofpbuf
*openflow
)
1214 struct ntr_group_prop_selection_method
*prop
;
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
);
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
)
1235 struct ofpbuf
*reply
= ofpbuf_from_list(ovs_list_back(replies
));
1236 struct ofp11_group_desc_stats
*ogds
;
1237 struct ofputil_bucket
*bucket
;
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
);
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
);
1250 ofpmp_postappend(replies
, start_ogds
);
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
)
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
;
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
);
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
);
1276 /* Add group properties */
1277 if (gds
->props
.selection_method
[0]) {
1278 ofputil_put_group_prop_ntr_selection_method(version
, &gds
->props
,
1281 ogds
= ofpbuf_at_assert(reply
, start_ogds
, sizeof *ogds
);
1282 ogds
->length
= htons(reply
->size
- start_ogds
);
1284 ofpmp_postappend(replies
, start_ogds
);
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(). */
1291 ofputil_append_group_desc_reply(const struct ofputil_group_desc
*gds
,
1292 const struct ovs_list
*buckets
,
1293 struct ovs_list
*replies
)
1295 enum ofp_version version
= ofpmp_version(replies
);
1303 ofputil_append_ofp11_group_desc_reply(gds
, buckets
, replies
, version
);
1308 ofputil_append_ofp15_group_desc_reply(gds
, buckets
, replies
, version
);
1317 ofputil_pull_ofp11_buckets(struct ofpbuf
*msg
, size_t buckets_length
,
1318 enum ofp_version version
, struct ovs_list
*buckets
)
1320 struct ofp11_bucket
*ob
;
1321 uint32_t bucket_id
= 0;
1323 ovs_list_init(buckets
);
1324 while (buckets_length
> 0) {
1325 struct ofputil_bucket
*bucket
;
1326 struct ofpbuf ofpacts
;
1330 ob
= (buckets_length
>= sizeof *ob
1331 ? ofpbuf_try_pull(msg
, sizeof *ob
)
1334 VLOG_WARN_RL(&rl
, "buckets end with %"PRIuSIZE
" leftover bytes",
1336 ofputil_bucket_list_destroy(buckets
);
1337 return OFPERR_OFPGMFC_BAD_BUCKET
;
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
;
1353 buckets_length
-= ob_len
;
1355 ofpbuf_init(&ofpacts
, 0);
1356 error
= ofpacts_pull_openflow_actions(msg
, ob_len
- sizeof *ob
,
1357 version
, NULL
, NULL
, &ofpacts
);
1359 ofpbuf_uninit(&ofpacts
);
1360 ofputil_bucket_list_destroy(buckets
);
1364 bucket
= xzalloc(sizeof *bucket
);
1365 bucket
->weight
= ntohs(ob
->weight
);
1366 error
= ofputil_port_from_ofp11(ob
->watch_port
, &bucket
->watch_port
);
1368 ofpbuf_uninit(&ofpacts
);
1369 ofputil_bucket_list_destroy(buckets
);
1371 return OFPERR_OFPGMFC_BAD_WATCH
;
1373 bucket
->watch_group
= ntohl(ob
->watch_group
);
1374 bucket
->bucket_id
= bucket_id
++;
1376 bucket
->ofpacts
= ofpbuf_steal_data(&ofpacts
);
1377 bucket
->ofpacts_len
= ofpacts
.size
;
1378 ovs_list_push_back(buckets
, &bucket
->list_node
);
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
)
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);
1399 ofpbuf_init(&ofpacts
, 0);
1401 struct ofp15_bucket
*ob
= ofpbuf_try_pull(msg
, sizeof *ob
);
1403 VLOG_WARN_RL(&rl
, "buckets end with %"PRIuSIZE
1404 " leftover bytes", buckets_length
);
1408 ob_len
= ntohs(ob
->len
);
1409 actions_len
= ntohs(ob
->action_array_len
);
1411 if (ob_len
< sizeof *ob
) {
1412 VLOG_WARN_RL(&rl
, "OpenFlow message bucket length "
1413 "%"PRIuSIZE
" is not valid", ob_len
);
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
);
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
);
1427 buckets_length
-= ob_len
;
1429 err
= ofpacts_pull_openflow_actions(msg
, actions_len
, version
,
1430 NULL
, NULL
, &ofpacts
);
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
;
1442 err
= ofpprop_pull(&properties
, &payload
, &type
);
1448 case OFPGBPT15_WEIGHT
:
1449 err
= ofpprop_parse_be16(&payload
, &weight
);
1452 case OFPGBPT15_WATCH_PORT
:
1453 err
= ofpprop_parse_be32(&payload
, &watch_port
);
1456 case OFPGBPT15_WATCH_GROUP
:
1457 err
= ofpprop_parse_be32(&payload
, &watch_group
);
1461 err
= OFPPROP_UNKNOWN(false, "group bucket", type
);
1470 bucket
= xzalloc(sizeof *bucket
);
1472 bucket
->weight
= ntohs(weight
);
1473 err
= ofputil_port_from_ofp11(watch_port
, &bucket
->watch_port
);
1475 err
= OFPERR_OFPGMFC_BAD_WATCH
;
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",
1483 err
= OFPERR_OFPGMFC_BAD_BUCKET
;
1487 bucket
->ofpacts
= ofpbuf_steal_data(&ofpacts
);
1488 bucket
->ofpacts_len
= ofpacts
.size
;
1489 ovs_list_push_back(buckets
, &bucket
->list_node
);
1495 ofpbuf_uninit(&ofpacts
);
1496 ofputil_bucket_list_destroy(buckets
);
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
;
1510 ofputil_init_group_properties(struct ofputil_group_props
*gp
)
1512 memset(gp
, 0, sizeof *gp
);
1516 ofputil_group_properties_copy(struct ofputil_group_props
*to
,
1517 const struct ofputil_group_props
*from
)
1520 to
->fields
.values
= xmemdup(from
->fields
.values
, from
->fields
.values_size
);
1524 ofputil_group_properties_destroy(struct ofputil_group_props
*gp
)
1526 free(gp
->fields
.values
);
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
)
1535 struct ntr_group_prop_selection_method
*prop
= payload
->data
;
1536 size_t fields_len
, method_len
;
1539 switch (group_type
) {
1540 case OFPGT11_SELECT
:
1543 case OFPGT11_INDIRECT
:
1545 OFPPROP_LOG(&rl
, false, "ntr selection method property is "
1546 "only allowed for select groups");
1547 return OFPERR_OFPBPC_BAD_VALUE
;
1549 return OFPERR_OFPGMFC_BAD_TYPE
;
1552 switch (group_cmd
) {
1554 case OFPGC15_MODIFY
:
1555 case OFPGC15_ADD_OR_MOD
:
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
;
1564 return OFPERR_OFPGMFC_BAD_COMMAND
;
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
;
1573 method_len
= strnlen(prop
->selection_method
, NTR_MAX_SELECTION_METHOD_LEN
);
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
;
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
;
1588 /* 'method_len' is now non-zero. */
1590 strcpy(gp
->selection_method
, prop
->selection_method
);
1591 gp
->selection_method_param
= ntohll(prop
->selection_method_param
);
1593 ofpbuf_pull(payload
, sizeof *prop
);
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
;
1602 if (fields_len
> 0) {
1603 error
= oxm_pull_field_array(payload
->data
, fields_len
,
1606 OFPPROP_LOG(&rl
, false,
1607 "ntr selection method fields are invalid");
1611 /* Selection_method "hash: w/o fields means default hash method. */
1612 gp
->fields
.values_size
= 0;
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
)
1625 struct ofpbuf properties
= ofpbuf_const_initializer(
1626 ofpbuf_pull(msg
, properties_len
), properties_len
);
1627 while (properties
.size
> 0) {
1628 struct ofpbuf payload
;
1632 error
= ofpprop_pull(&properties
, &payload
, &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
,
1645 error
= OFPPROP_UNKNOWN(false, "group", type
);
1658 ofputil_decode_ofp11_group_desc_reply(struct ofputil_group_desc
*gd
,
1660 enum ofp_version version
)
1662 struct ofp11_group_desc_stats
*ogds
;
1666 ofpraw_pull_assert(msg
);
1673 ogds
= ofpbuf_try_pull(msg
, sizeof *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
;
1679 gd
->type
= ogds
->type
;
1680 gd
->group_id
= ntohl(ogds
->group_id
);
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
;
1689 return ofputil_pull_ofp11_buckets(msg
, length
- sizeof *ogds
, version
,
1694 ofputil_decode_ofp15_group_desc_reply(struct ofputil_group_desc
*gd
,
1696 enum ofp_version version
)
1698 struct ofp15_group_desc_stats
*ogds
;
1699 uint16_t length
, bucket_list_len
;
1703 ofpraw_pull_assert(msg
);
1710 ogds
= ofpbuf_try_pull(msg
, sizeof *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
;
1716 gd
->type
= ogds
->type
;
1717 gd
->group_id
= ntohl(ogds
->group_id
);
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
;
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
;
1732 error
= ofputil_pull_ofp15_buckets(msg
, bucket_list_len
, version
, gd
->type
,
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
);
1750 ofputil_uninit_group_desc(gd
);
1755 /* Converts a group description reply in 'msg' into an abstract
1756 * ofputil_group_desc in 'gd'.
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.
1763 * Returns 0 if successful, EOF if no replies were left in this 'msg',
1764 * otherwise a positive errno value. */
1766 ofputil_decode_group_desc_reply(struct ofputil_group_desc
*gd
,
1767 struct ofpbuf
*msg
, enum ofp_version version
)
1769 ofputil_init_group_properties(&gd
->props
);
1777 return ofputil_decode_ofp11_group_desc_reply(gd
, msg
, version
);
1781 return ofputil_decode_ofp15_group_desc_reply(gd
, msg
, version
);
1789 ofp_print_bucket_id(struct ds
*s
, const char *label
, uint32_t bucket_id
,
1790 enum ofp_version ofp_version
)
1792 if (ofp_version
> OFP10_VERSION
&& ofp_version
< OFP15_VERSION
) {
1796 ds_put_cstr(s
, label
);
1798 switch (bucket_id
) {
1799 case OFPG15_BUCKET_FIRST
:
1800 ds_put_cstr(s
, "first");
1802 case OFPG15_BUCKET_LAST
:
1803 ds_put_cstr(s
, "last");
1805 case OFPG15_BUCKET_ALL
:
1806 ds_put_cstr(s
, "all");
1809 ds_put_format(s
, "%"PRIu32
, bucket_id
);
1813 ds_put_char(s
, ',');
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
)
1824 struct ofputil_bucket
*bucket
;
1826 ds_put_format(s
, "group_id=%"PRIu32
, group_id
);
1828 if (!suppress_type
) {
1829 static const char *type_str
[] = { "all", "select", "indirect",
1831 ds_put_format(s
, ",type=%s", type_str
[type
> 4 ? 4 : type
]);
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
);
1841 size_t n
= bitmap_count1(props
->fields
.used
.bm
, MFF_N_IDS
);
1843 ds_put_cstr(s
, ",fields=");
1844 oxm_format_field_array(s
, &props
->fields
);
1846 ds_put_cstr(s
, ",fields(");
1847 oxm_format_field_array(s
, &props
->fields
);
1848 ds_put_char(s
, ')');
1856 ds_put_char(s
, ',');
1858 LIST_FOR_EACH (bucket
, list_node
, p_buckets
) {
1859 ds_put_cstr(s
, "bucket=");
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
);
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
, ',');
1870 if (bucket
->watch_group
!= OFPG_ANY
) {
1871 ds_put_format(s
, "watch_group:%"PRIu32
",", bucket
->watch_group
);
1874 ds_put_cstr(s
, "actions=");
1875 struct ofpact_format_params fp
= {
1876 .port_map
= port_map
,
1877 .table_map
= table_map
,
1880 ofpacts_format(bucket
->ofpacts
, bucket
->ofpacts_len
, &fp
);
1881 ds_put_char(s
, ',');
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
)
1892 struct ofpbuf b
= ofpbuf_const_initializer(oh
, ntohs(oh
->length
));
1894 struct ofputil_group_desc gd
;
1897 retval
= ofputil_decode_group_desc_reply(&gd
, &b
, oh
->version
);
1899 return retval
!= EOF
? retval
: 0;
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
);
1911 ofputil_uninit_group_mod(struct ofputil_group_mod
*gm
)
1913 ofputil_bucket_list_destroy(&gm
->buckets
);
1914 ofputil_group_properties_destroy(&gm
->props
);
1918 bad_group_cmd(enum ofp15_group_mod_command cmd
)
1920 const char *opt_version
;
1921 const char *version
;
1922 const char *cmd_str
;
1926 case OFPGC15_MODIFY
:
1927 case OFPGC15_ADD_OR_MOD
:
1928 case OFPGC15_DELETE
:
1933 case OFPGC15_INSERT_BUCKET
:
1934 case OFPGC15_REMOVE_BUCKET
:
1945 cmd_str
= "add-group";
1948 case OFPGC15_MODIFY
:
1949 case OFPGC15_ADD_OR_MOD
:
1950 cmd_str
= "mod-group";
1953 case OFPGC15_DELETE
:
1954 cmd_str
= "del-group";
1957 case OFPGC15_INSERT_BUCKET
:
1958 cmd_str
= "insert-bucket";
1961 case OFPGC15_REMOVE_BUCKET
:
1962 cmd_str
= "remove-bucket";
1969 ovs_fatal(0, "%s needs OpenFlow %s or later (\'-O OpenFlow%s\')",
1970 cmd_str
, version
, opt_version
);
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
,
1981 struct ofp11_group_mod
*ogm
;
1983 struct ofputil_bucket
*bucket
;
1985 b
= ofpraw_alloc(OFPRAW_OFPT11_GROUP_MOD
, ofp_version
, 0);
1986 start_ogm
= b
->size
;
1987 ofpbuf_put_zeros(b
, sizeof *ogm
);
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
:
1995 bad_group_cmd(gm
->command
);
1997 command
= OFPGC11_MODIFY
;
1998 buckets
= new_buckets
;
2001 case OFPGC11_ADD_OR_MOD
:
2002 if (group_existed
>= 0) {
2003 command
= group_existed
? OFPGC11_MODIFY
: OFPGC11_ADD
;
2010 LIST_FOR_EACH (bucket
, list_node
, buckets
) {
2011 ofputil_put_ofp11_bucket(bucket
, b
, ofp_version
);
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
);
2018 ofpmsg_update_length(b
);
2023 static struct ofpbuf
*
2024 ofputil_encode_ofp15_group_mod(enum ofp_version ofp_version
,
2025 const struct ofputil_group_mod
*gm
,
2029 struct ofp15_group_mod
*ogm
;
2031 struct ofputil_bucket
*bucket
;
2032 struct id_pool
*bucket_ids
= NULL
;
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
);
2040 LIST_FOR_EACH (bucket
, list_node
, &gm
->buckets
) {
2043 /* Generate a bucket id if none was supplied */
2044 if (bucket
->bucket_id
> OFPG15_BUCKET_MAX
) {
2046 const struct ofputil_bucket
*bkt
;
2048 bucket_ids
= id_pool_create(0, OFPG15_BUCKET_MAX
+ 1);
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
) {
2056 if (bkt
->bucket_id
<= OFPG15_BUCKET_MAX
) {
2057 id_pool_add(bucket_ids
, bkt
->bucket_id
);
2062 if (!id_pool_alloc_id(bucket_ids
, &bucket_id
)) {
2066 bucket_id
= bucket
->bucket_id
;
2069 ofputil_put_ofp15_bucket(bucket
, bucket_id
, gm
->type
, b
, ofp_version
);
2071 ogm
= ofpbuf_at_assert(b
, start_ogm
, sizeof *ogm
);
2072 ogm
->command
= htons(gm
->command
!= OFPGC11_ADD_OR_MOD
|| group_existed
< 0
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
);
2080 /* Add group properties */
2081 if (gm
->props
.selection_method
[0]) {
2082 ofputil_put_group_prop_ntr_selection_method(ofp_version
, &gm
->props
, b
);
2085 id_pool_destroy(bucket_ids
);
2086 ofpmsg_update_length(b
);
2090 /* Converts abstract group mod 'gm' into a message for OpenFlow version
2091 * 'ofp_version' and returns the message.
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().
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. */
2104 ofputil_encode_group_mod(enum ofp_version ofp_version
,
2105 const struct ofputil_group_mod
*gm
,
2106 const struct ovs_list
*new_buckets
,
2110 switch (ofp_version
) {
2115 return ofputil_encode_ofp11_group_mod(ofp_version
, gm
,
2116 new_buckets
, group_existed
);
2120 return ofputil_encode_ofp15_group_mod(ofp_version
, gm
, group_existed
);
2128 ofputil_pull_ofp11_group_mod(struct ofpbuf
*msg
, enum ofp_version ofp_version
,
2129 struct ofputil_group_mod
*gm
)
2131 const struct ofp11_group_mod
*ogm
;
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
;
2140 error
= ofputil_pull_ofp11_buckets(msg
, msg
->size
, ofp_version
,
2143 /* OF1.3.5+ prescribes an error when an OFPGC_DELETE includes buckets. */
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
);
2156 ofputil_pull_ofp15_group_mod(struct ofpbuf
*msg
, enum ofp_version ofp_version
,
2157 struct ofputil_group_mod
*gm
)
2159 const struct ofp15_group_mod
*ogm
;
2160 uint16_t bucket_list_len
;
2161 enum ofperr error
= OFPERR_OFPGMFC_BAD_BUCKET
;
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
);
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
) {
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
) {
2184 case OFPGC11_MODIFY
:
2185 case OFPGC11_ADD_OR_MOD
:
2186 case OFPGC11_DELETE
:
2188 if (gm
->command_bucket_id
== OFPG15_BUCKET_ALL
) {
2195 "group command bucket id (%u) is out of range",
2196 gm
->command_bucket_id
);
2197 return OFPERR_OFPGMFC_BAD_BUCKET
;
2200 bucket_list_len
= ntohs(ogm
->bucket_array_len
);
2201 if (bucket_list_len
> msg
->size
) {
2202 return OFPERR_OFPBRC_BAD_LEN
;
2204 error
= ofputil_pull_ofp15_buckets(msg
, bucket_list_len
, ofp_version
,
2205 gm
->type
, &gm
->buckets
);
2210 error
= parse_ofp15_group_properties(msg
, gm
->type
, gm
->command
,
2211 &gm
->props
, msg
->size
);
2213 ofputil_uninit_group_mod(gm
);
2219 ofputil_check_group_mod(const struct ofputil_group_mod
*gm
)
2222 case OFPGT11_INDIRECT
:
2223 if (gm
->command
!= OFPGC11_DELETE
2224 && !ovs_list_is_singleton(&gm
->buckets
) ) {
2225 return OFPERR_OFPGMFC_INVALID_GROUP
;
2229 case OFPGT11_SELECT
:
2233 return OFPERR_OFPGMFC_BAD_TYPE
;
2236 switch (gm
->command
) {
2238 case OFPGC11_MODIFY
:
2239 case OFPGC11_ADD_OR_MOD
:
2240 case OFPGC11_DELETE
:
2241 case OFPGC15_INSERT_BUCKET
:
2243 case OFPGC15_REMOVE_BUCKET
:
2244 if (!ovs_list_is_empty(&gm
->buckets
)) {
2245 return OFPERR_OFPGMFC_BAD_BUCKET
;
2249 return OFPERR_OFPGMFC_BAD_COMMAND
;
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
;
2261 case OFPGT11_INDIRECT
:
2262 if (ofputil_bucket_has_liveness(bucket
)) {
2263 return OFPERR_OFPGMFC_WATCH_UNSUPPORTED
;
2266 case OFPGT11_SELECT
:
2269 if (!ofputil_bucket_has_liveness(bucket
)) {
2270 return OFPERR_OFPGMFC_INVALID_GROUP
;
2274 /* Returning BAD TYPE to be consistent
2275 * though gm->type has been checked already. */
2276 return OFPERR_OFPGMFC_BAD_TYPE
;
2283 /* Converts OpenFlow group mod message 'oh' into an abstract group mod in
2284 * 'gm'. Returns 0 if successful, otherwise an OpenFlow error code. */
2286 ofputil_decode_group_mod(const struct ofp_header
*oh
,
2287 struct ofputil_group_mod
*gm
)
2289 ofputil_init_group_properties(&gm
->props
);
2291 enum ofp_version ofp_version
= oh
->version
;
2292 struct ofpbuf msg
= ofpbuf_const_initializer(oh
, ntohs(oh
->length
));
2293 ofpraw_pull_assert(&msg
);
2296 switch (ofp_version
) {
2301 err
= ofputil_pull_ofp11_group_mod(&msg
, ofp_version
, gm
);
2306 err
= ofputil_pull_ofp15_group_mod(&msg
, ofp_version
, gm
);
2316 err
= ofputil_check_group_mod(gm
);
2318 ofputil_uninit_group_mod(gm
);
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
)
2329 bool bucket_command
= false;
2331 ds_put_char(s
, '\n');
2333 ds_put_char(s
, ' ');
2334 switch (gm
->command
) {
2336 ds_put_cstr(s
, "ADD");
2339 case OFPGC11_MODIFY
:
2340 ds_put_cstr(s
, "MOD");
2343 case OFPGC11_ADD_OR_MOD
:
2344 ds_put_cstr(s
, "ADD_OR_MOD");
2347 case OFPGC11_DELETE
:
2348 ds_put_cstr(s
, "DEL");
2351 case OFPGC15_INSERT_BUCKET
:
2352 ds_put_cstr(s
, "INSERT_BUCKET");
2353 bucket_command
= true;
2356 case OFPGC15_REMOVE_BUCKET
:
2357 ds_put_cstr(s
, "REMOVE_BUCKET");
2358 bucket_command
= true;
2362 ds_put_format(s
, "cmd:%"PRIu16
"", gm
->command
);
2364 ds_put_char(s
, ' ');
2366 if (bucket_command
) {
2367 ofp_print_bucket_id(s
, "command_bucket_id:",
2368 gm
->command_bucket_id
, ofp_version
);
2371 ofp_print_group(s
, gm
->group_id
, gm
->type
, &gm
->buckets
, &gm
->props
,
2372 ofp_version
, bucket_command
, port_map
, table_map
);
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
)
2380 struct ofputil_group_mod gm
;
2383 error
= ofputil_decode_group_mod(oh
, &gm
);
2387 ofputil_group_mod_format__(s
, oh
->version
, &gm
, port_map
, table_map
);
2388 ofputil_uninit_group_mod(&gm
);