2 * Copyright (c) 2008-2017 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-table.h"
21 #include "openvswitch/dynamic-string.h"
22 #include "openvswitch/json.h"
23 #include "openvswitch/ofp-actions.h"
24 #include "openvswitch/ofp-msgs.h"
25 #include "openvswitch/ofp-print.h"
26 #include "openvswitch/ofp-prop.h"
27 #include "openvswitch/ofpbuf.h"
28 #include "openvswitch/vlog.h"
31 VLOG_DEFINE_THIS_MODULE(ofp_table
);
33 static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(1, 5);
35 static ovs_be32
ofputil_encode_table_config(enum ofputil_table_miss
,
36 enum ofputil_table_eviction
,
37 enum ofputil_table_vacancy
,
39 static enum ofputil_table_vacancy
ofputil_decode_table_vacancy(
40 ovs_be32 config
, enum ofp_version
);
41 static enum ofputil_table_eviction
ofputil_decode_table_eviction(
42 ovs_be32 config
, enum ofp_version
);
45 ofputil_table_miss_to_string(enum ofputil_table_miss miss
)
48 case OFPUTIL_TABLE_MISS_DEFAULT
: return "default";
49 case OFPUTIL_TABLE_MISS_CONTROLLER
: return "controller";
50 case OFPUTIL_TABLE_MISS_CONTINUE
: return "continue";
51 case OFPUTIL_TABLE_MISS_DROP
: return "drop";
52 default: return "***error***";
57 ofputil_table_eviction_to_string(enum ofputil_table_eviction eviction
)
60 case OFPUTIL_TABLE_EVICTION_DEFAULT
: return "default";
61 case OFPUTIL_TABLE_EVICTION_ON
: return "on";
62 case OFPUTIL_TABLE_EVICTION_OFF
: return "off";
63 default: return "***error***";
68 ofputil_table_vacancy_to_string(enum ofputil_table_vacancy vacancy
)
71 case OFPUTIL_TABLE_VACANCY_DEFAULT
: return "default";
72 case OFPUTIL_TABLE_VACANCY_ON
: return "on";
73 case OFPUTIL_TABLE_VACANCY_OFF
: return "off";
74 default: return "***error***";
78 /* ofputil_table_map. */
81 ofputil_table_map_init(struct ofputil_table_map
*map
)
83 namemap_init(&map
->map
);
87 ofputil_table_map_put(struct ofputil_table_map
*map
,
88 uint8_t table_id
, const char *name
)
90 namemap_put(&map
->map
, table_id
, name
);
94 ofputil_table_map_get_name(const struct ofputil_table_map
*map
,
97 struct namemap_node
*node
98 = map
? namemap_find_by_number(&map
->map
, table_id
) : NULL
;
99 return node
&& !node
->duplicate
? node
->name
: NULL
;
103 ofputil_table_map_get_number(const struct ofputil_table_map
*map
,
106 struct namemap_node
*node
107 = map
? namemap_find_by_name(&map
->map
, name
) : NULL
;
108 return node
&& !node
->duplicate
? node
->number
: UINT8_MAX
;
112 ofputil_table_map_destroy(struct ofputil_table_map
*map
)
114 namemap_destroy(&map
->map
);
119 /* Stores the table number represented by 's' into '*tablep'. 's' may be an
120 * integer or, if 'table_map' is nonnull, a name (quoted or unquoted).
122 * Returns true if successful, false if 's' is not a valid OpenFlow table
123 * number or name. The caller should issue an error message in this case,
124 * because this function usually does not. (This gives the caller an
125 * opportunity to look up the table name another way, e.g. by contacting the
126 * switch and listing the names of all its tables). */
128 ofputil_table_from_string(const char *s
,
129 const struct ofputil_table_map
*table_map
,
134 VLOG_WARN("Negative value %s is not a valid table number.", s
);
139 if (str_to_uint(s
, 10, &table
)) {
141 VLOG_WARN("table %u is outside the supported range 0 through 255",
149 table
= ofputil_table_map_get_number(table_map
, s
);
151 size_t length
= strlen(s
);
154 && s
[length
- 1] == '"'
155 && json_string_unescape(s
+ 1, length
- 2, &name
)) {
156 table
= ofputil_table_map_get_number(table_map
, name
);
160 if (table
!= UINT8_MAX
) {
169 /* Appends to 's' a string representation of the OpenFlow table number 'table',
170 * either the table number or a name drawn from 'table_map'. */
172 ofputil_format_table(uint8_t table
, const struct ofputil_table_map
*table_map
,
175 const char *table_name
= ofputil_table_map_get_name(table_map
, table
);
177 namemap_put_name(table_name
, s
);
179 ds_put_format(s
, "%"PRIu8
, table
);
183 /* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string
184 * representation of OpenFlow table number 'table', either the table's number
185 * or a name drawn from 'table_map'. */
187 ofputil_table_to_string(uint8_t table
,
188 const struct ofputil_table_map
*table_map
,
189 char *namebuf
, size_t bufsize
)
191 const char *table_name
= ofputil_table_map_get_name(table_map
, table
);
193 struct ds s
= DS_EMPTY_INITIALIZER
;
194 namemap_put_name(table_name
, &s
);
195 ovs_strlcpy(namebuf
, ds_cstr(&s
), bufsize
);
200 snprintf(namebuf
, bufsize
, "%"PRIu8
, table
);
203 /* Table features. */
206 pull_table_feature_property(struct ofpbuf
*msg
, struct ofpbuf
*payload
,
211 error
= ofpprop_pull(msg
, payload
, typep
);
212 if (payload
&& !error
) {
213 ofpbuf_pull(payload
, (char *)payload
->msg
- (char *)payload
->header
);
219 parse_action_bitmap(struct ofpbuf
*payload
, enum ofp_version ofp_version
,
224 while (payload
->size
> 0) {
228 error
= ofpprop_pull__(payload
, NULL
, 1, 0x10000, &type
);
232 if (type
< CHAR_BIT
* sizeof types
) {
237 *ofpacts
= ofpact_bitmap_from_openflow(htonl(types
), ofp_version
);
242 parse_instruction_ids(struct ofpbuf
*payload
, bool loose
, uint32_t *insts
)
245 while (payload
->size
> 0) {
246 enum ovs_instruction_type inst
;
250 /* OF1.3 and OF1.4 aren't clear about padding in the instruction IDs.
251 * It seems clear that they aren't padded to 8 bytes, though, because
252 * both standards say that "non-experimenter instructions are 4 bytes"
253 * and do not mention any padding before the first instruction ID.
254 * (There wouldn't be any point in padding to 8 bytes if the IDs were
255 * aligned on an odd 4-byte boundary.)
257 * Anyway, we just assume they're all glommed together on byte
259 error
= ofpprop_pull__(payload
, NULL
, 1, 0x10000, &ofpit
);
264 error
= ovs_instruction_type_from_inst_type(&inst
, ofpit
);
266 *insts
|= 1u << inst
;
275 parse_table_features_next_table(struct ofpbuf
*payload
,
276 unsigned long int *next_tables
)
280 memset(next_tables
, 0, bitmap_n_bytes(255));
281 for (i
= 0; i
< payload
->size
; i
++) {
282 uint8_t id
= ((const uint8_t *) payload
->data
)[i
];
284 return OFPERR_OFPBPC_BAD_VALUE
;
286 bitmap_set1(next_tables
, id
);
292 parse_oxms(struct ofpbuf
*payload
, bool loose
,
293 struct mf_bitmap
*exactp
, struct mf_bitmap
*maskedp
)
295 struct mf_bitmap exact
= MF_BITMAP_INITIALIZER
;
296 struct mf_bitmap masked
= MF_BITMAP_INITIALIZER
;
298 while (payload
->size
> 0) {
299 const struct mf_field
*field
;
303 error
= nx_pull_header(payload
, NULL
, &field
, &hasmask
);
305 bitmap_set1(hasmask
? masked
.bm
: exact
.bm
, field
->id
);
306 } else if (error
!= OFPERR_OFPBMC_BAD_FIELD
|| !loose
) {
312 } else if (!bitmap_is_all_zeros(exact
.bm
, MFF_N_IDS
)) {
313 return OFPERR_OFPBMC_BAD_MASK
;
317 } else if (!bitmap_is_all_zeros(masked
.bm
, MFF_N_IDS
)) {
318 return OFPERR_OFPBMC_BAD_MASK
;
323 /* Converts an OFPMP_TABLE_FEATURES request or reply in 'msg' into an abstract
324 * ofputil_table_features in 'tf'.
326 * If 'loose' is true, this function ignores properties and values that it does
327 * not understand, as a controller would want to do when interpreting
328 * capabilities provided by a switch. If 'loose' is false, this function
329 * treats unknown properties and values as an error, as a switch would want to
330 * do when interpreting a configuration request made by a controller.
332 * A single OpenFlow message can specify features for multiple tables. Calling
333 * this function multiple times for a single 'msg' iterates through the tables
334 * in the message. The caller must initially leave 'msg''s layer pointers null
335 * and not modify them between calls.
337 * Returns 0 if successful, EOF if no tables were left in this 'msg', otherwise
338 * a positive "enum ofperr" value. */
340 ofputil_decode_table_features(struct ofpbuf
*msg
,
341 struct ofputil_table_features
*tf
, bool loose
)
343 memset(tf
, 0, sizeof *tf
);
346 ofpraw_pull_assert(msg
);
353 const struct ofp_header
*oh
= msg
->header
;
354 struct ofp13_table_features
*otf
= msg
->data
;
355 if (msg
->size
< sizeof *otf
) {
356 return OFPERR_OFPBPC_BAD_LEN
;
359 unsigned int len
= ntohs(otf
->length
);
360 if (len
< sizeof *otf
|| len
% 8 || len
> msg
->size
) {
361 return OFPERR_OFPBPC_BAD_LEN
;
364 tf
->table_id
= otf
->table_id
;
365 if (tf
->table_id
== OFPTT_ALL
) {
366 return OFPERR_OFPTFFC_BAD_TABLE
;
369 ovs_strlcpy_arrays(tf
->name
, otf
->name
);
370 tf
->metadata_match
= otf
->metadata_match
;
371 tf
->metadata_write
= otf
->metadata_write
;
372 tf
->miss_config
= OFPUTIL_TABLE_MISS_DEFAULT
;
373 if (oh
->version
>= OFP14_VERSION
) {
374 uint32_t caps
= ntohl(otf
->capabilities
);
375 tf
->supports_eviction
= (caps
& OFPTC14_EVICTION
) != 0;
376 tf
->supports_vacancy_events
= (caps
& OFPTC14_VACANCY_EVENTS
) != 0;
378 tf
->supports_eviction
= -1;
379 tf
->supports_vacancy_events
= -1;
381 tf
->max_entries
= ntohl(otf
->max_entries
);
383 struct ofpbuf properties
= ofpbuf_const_initializer(ofpbuf_pull(msg
, len
),
385 ofpbuf_pull(&properties
, sizeof *otf
);
386 while (properties
.size
> 0) {
387 struct ofpbuf payload
;
391 error
= pull_table_feature_property(&properties
, &payload
, &type
);
396 switch ((enum ofp13_table_feature_prop_type
) type
) {
397 case OFPTFPT13_INSTRUCTIONS
:
398 error
= parse_instruction_ids(&payload
, loose
,
399 &tf
->nonmiss
.instructions
);
401 case OFPTFPT13_INSTRUCTIONS_MISS
:
402 error
= parse_instruction_ids(&payload
, loose
,
403 &tf
->miss
.instructions
);
406 case OFPTFPT13_NEXT_TABLES
:
407 error
= parse_table_features_next_table(&payload
,
410 case OFPTFPT13_NEXT_TABLES_MISS
:
411 error
= parse_table_features_next_table(&payload
, tf
->miss
.next
);
414 case OFPTFPT13_WRITE_ACTIONS
:
415 error
= parse_action_bitmap(&payload
, oh
->version
,
416 &tf
->nonmiss
.write
.ofpacts
);
418 case OFPTFPT13_WRITE_ACTIONS_MISS
:
419 error
= parse_action_bitmap(&payload
, oh
->version
,
420 &tf
->miss
.write
.ofpacts
);
423 case OFPTFPT13_APPLY_ACTIONS
:
424 error
= parse_action_bitmap(&payload
, oh
->version
,
425 &tf
->nonmiss
.apply
.ofpacts
);
427 case OFPTFPT13_APPLY_ACTIONS_MISS
:
428 error
= parse_action_bitmap(&payload
, oh
->version
,
429 &tf
->miss
.apply
.ofpacts
);
432 case OFPTFPT13_MATCH
:
433 error
= parse_oxms(&payload
, loose
, &tf
->match
, &tf
->mask
);
435 case OFPTFPT13_WILDCARDS
:
436 error
= parse_oxms(&payload
, loose
, &tf
->wildcard
, NULL
);
439 case OFPTFPT13_WRITE_SETFIELD
:
440 error
= parse_oxms(&payload
, loose
,
441 &tf
->nonmiss
.write
.set_fields
, NULL
);
443 case OFPTFPT13_WRITE_SETFIELD_MISS
:
444 error
= parse_oxms(&payload
, loose
,
445 &tf
->miss
.write
.set_fields
, NULL
);
447 case OFPTFPT13_APPLY_SETFIELD
:
448 error
= parse_oxms(&payload
, loose
,
449 &tf
->nonmiss
.apply
.set_fields
, NULL
);
451 case OFPTFPT13_APPLY_SETFIELD_MISS
:
452 error
= parse_oxms(&payload
, loose
,
453 &tf
->miss
.apply
.set_fields
, NULL
);
456 case OFPTFPT13_EXPERIMENTER
:
457 case OFPTFPT13_EXPERIMENTER_MISS
:
459 error
= OFPPROP_UNKNOWN(loose
, "table features", type
);
467 /* Fix inconsistencies:
469 * - Turn on 'match' bits that are set in 'mask', because maskable
470 * fields are matchable.
472 * - Turn on 'wildcard' bits that are set in 'mask', because a field
473 * that is arbitrarily maskable can be wildcarded entirely.
475 * - Turn off 'wildcard' bits that are not in 'match', because a field
476 * must be matchable for it to be meaningfully wildcarded. */
477 bitmap_or(tf
->match
.bm
, tf
->mask
.bm
, MFF_N_IDS
);
478 bitmap_or(tf
->wildcard
.bm
, tf
->mask
.bm
, MFF_N_IDS
);
479 bitmap_and(tf
->wildcard
.bm
, tf
->match
.bm
, MFF_N_IDS
);
484 /* Encodes and returns a request to obtain the table features of a switch.
485 * The message is encoded for OpenFlow version 'ofp_version'. */
487 ofputil_encode_table_features_request(enum ofp_version ofp_version
)
489 struct ofpbuf
*request
= NULL
;
491 switch (ofp_version
) {
495 ovs_fatal(0, "dump-table-features needs OpenFlow 1.3 or later "
496 "(\'-O OpenFlow13\')");
501 request
= ofpraw_alloc(OFPRAW_OFPST13_TABLE_FEATURES_REQUEST
,
512 put_fields_property(struct ofpbuf
*reply
,
513 const struct mf_bitmap
*fields
,
514 const struct mf_bitmap
*masks
,
515 enum ofp13_table_feature_prop_type property
,
516 enum ofp_version version
)
521 start_ofs
= ofpprop_start(reply
, property
);
522 BITMAP_FOR_EACH_1 (field
, MFF_N_IDS
, fields
->bm
) {
523 nx_put_header(reply
, field
, version
,
524 masks
&& bitmap_is_set(masks
->bm
, field
));
526 ofpprop_end(reply
, start_ofs
);
530 put_table_action_features(struct ofpbuf
*reply
,
531 const struct ofputil_table_action_features
*taf
,
532 enum ofp13_table_feature_prop_type actions_type
,
533 enum ofp13_table_feature_prop_type set_fields_type
,
534 int miss_offset
, enum ofp_version version
)
536 ofpprop_put_bitmap(reply
, actions_type
+ miss_offset
,
537 ntohl(ofpact_bitmap_to_openflow(taf
->ofpacts
,
539 put_fields_property(reply
, &taf
->set_fields
, NULL
,
540 set_fields_type
+ miss_offset
, version
);
544 put_table_instruction_features(
545 struct ofpbuf
*reply
, const struct ofputil_table_instruction_features
*tif
,
546 int miss_offset
, enum ofp_version version
)
551 ofpprop_put_bitmap(reply
, OFPTFPT13_INSTRUCTIONS
+ miss_offset
,
552 ntohl(ovsinst_bitmap_to_openflow(tif
->instructions
,
555 start_ofs
= ofpprop_start(reply
, OFPTFPT13_NEXT_TABLES
+ miss_offset
);
556 BITMAP_FOR_EACH_1 (table_id
, 255, tif
->next
) {
557 ofpbuf_put(reply
, &table_id
, 1);
559 ofpprop_end(reply
, start_ofs
);
561 put_table_action_features(reply
, &tif
->write
,
562 OFPTFPT13_WRITE_ACTIONS
,
563 OFPTFPT13_WRITE_SETFIELD
, miss_offset
, version
);
564 put_table_action_features(reply
, &tif
->apply
,
565 OFPTFPT13_APPLY_ACTIONS
,
566 OFPTFPT13_APPLY_SETFIELD
, miss_offset
, version
);
570 ofputil_append_table_features_reply(const struct ofputil_table_features
*tf
,
571 struct ovs_list
*replies
)
573 struct ofpbuf
*reply
= ofpbuf_from_list(ovs_list_back(replies
));
574 enum ofp_version version
= ofpmp_version(replies
);
575 size_t start_ofs
= reply
->size
;
576 struct ofp13_table_features
*otf
;
578 otf
= ofpbuf_put_zeros(reply
, sizeof *otf
);
579 otf
->table_id
= tf
->table_id
;
580 ovs_strlcpy_arrays(otf
->name
, tf
->name
);
581 otf
->metadata_match
= tf
->metadata_match
;
582 otf
->metadata_write
= tf
->metadata_write
;
583 if (version
>= OFP14_VERSION
) {
584 if (tf
->supports_eviction
) {
585 otf
->capabilities
|= htonl(OFPTC14_EVICTION
);
587 if (tf
->supports_vacancy_events
) {
588 otf
->capabilities
|= htonl(OFPTC14_VACANCY_EVENTS
);
591 otf
->max_entries
= htonl(tf
->max_entries
);
593 put_table_instruction_features(reply
, &tf
->nonmiss
, 0, version
);
594 put_table_instruction_features(reply
, &tf
->miss
, 1, version
);
596 put_fields_property(reply
, &tf
->match
, &tf
->mask
,
597 OFPTFPT13_MATCH
, version
);
598 put_fields_property(reply
, &tf
->wildcard
, NULL
,
599 OFPTFPT13_WILDCARDS
, version
);
601 otf
= ofpbuf_at_assert(reply
, start_ofs
, sizeof *otf
);
602 otf
->length
= htons(reply
->size
- start_ofs
);
603 ofpmp_postappend(replies
, start_ofs
);
607 parse_table_desc_vacancy_property(struct ofpbuf
*property
,
608 struct ofputil_table_desc
*td
)
610 struct ofp14_table_mod_prop_vacancy
*otv
= property
->data
;
612 if (property
->size
!= sizeof *otv
) {
613 return OFPERR_OFPBPC_BAD_LEN
;
616 td
->table_vacancy
.vacancy_down
= otv
->vacancy_down
;
617 td
->table_vacancy
.vacancy_up
= otv
->vacancy_up
;
618 td
->table_vacancy
.vacancy
= otv
->vacancy
;
622 /* Decodes the next OpenFlow "table desc" message (of possibly several) from
623 * 'msg' into an abstract form in '*td'. Returns 0 if successful, EOF if the
624 * last "table desc" in 'msg' was already decoded, otherwise an OFPERR_*
627 ofputil_decode_table_desc(struct ofpbuf
*msg
,
628 struct ofputil_table_desc
*td
,
629 enum ofp_version version
)
631 memset(td
, 0, sizeof *td
);
634 ofpraw_pull_assert(msg
);
641 struct ofp14_table_desc
*otd
= ofpbuf_try_pull(msg
, sizeof *otd
);
643 VLOG_WARN_RL(&rl
, "OFP14_TABLE_DESC reply has %"PRIu32
" "
644 "leftover bytes at end", msg
->size
);
645 return OFPERR_OFPBRC_BAD_LEN
;
648 td
->table_id
= otd
->table_id
;
649 size_t length
= ntohs(otd
->length
);
650 if (length
< sizeof *otd
|| length
- sizeof *otd
> msg
->size
) {
651 VLOG_WARN_RL(&rl
, "OFP14_TABLE_DESC reply claims invalid "
652 "length %"PRIuSIZE
, length
);
653 return OFPERR_OFPBRC_BAD_LEN
;
655 length
-= sizeof *otd
;
657 td
->eviction
= ofputil_decode_table_eviction(otd
->config
, version
);
658 td
->vacancy
= ofputil_decode_table_vacancy(otd
->config
, version
);
659 td
->eviction_flags
= UINT32_MAX
;
661 struct ofpbuf properties
= ofpbuf_const_initializer(
662 ofpbuf_pull(msg
, length
), length
);
663 while (properties
.size
> 0) {
664 struct ofpbuf payload
;
668 error
= ofpprop_pull(&properties
, &payload
, &type
);
674 case OFPTMPT14_EVICTION
:
675 error
= ofpprop_parse_u32(&payload
, &td
->eviction_flags
);
678 case OFPTMPT14_VACANCY
:
679 error
= parse_table_desc_vacancy_property(&payload
, td
);
683 error
= OFPPROP_UNKNOWN(true, "table_desc", type
);
695 /* Encodes and returns a request to obtain description of tables of a switch.
696 * The message is encoded for OpenFlow version 'ofp_version'. */
698 ofputil_encode_table_desc_request(enum ofp_version ofp_version
)
700 struct ofpbuf
*request
= NULL
;
702 if (ofp_version
>= OFP14_VERSION
) {
703 request
= ofpraw_alloc(OFPRAW_OFPST14_TABLE_DESC_REQUEST
,
706 ovs_fatal(0, "dump-table-desc needs OpenFlow 1.4 or later "
707 "(\'-O OpenFlow14\')");
713 /* Function to append Table desc information in a reply list. */
715 ofputil_append_table_desc_reply(const struct ofputil_table_desc
*td
,
716 struct ovs_list
*replies
,
717 enum ofp_version version
)
719 struct ofpbuf
*reply
= ofpbuf_from_list(ovs_list_back(replies
));
721 struct ofp14_table_desc
*otd
;
723 start_otd
= reply
->size
;
724 ofpbuf_put_zeros(reply
, sizeof *otd
);
725 if (td
->eviction_flags
!= UINT32_MAX
) {
726 ofpprop_put_u32(reply
, OFPTMPT14_EVICTION
, td
->eviction_flags
);
728 if (td
->vacancy
== OFPUTIL_TABLE_VACANCY_ON
) {
729 struct ofp14_table_mod_prop_vacancy
*otv
;
731 otv
= ofpprop_put_zeros(reply
, OFPTMPT14_VACANCY
, sizeof *otv
);
732 otv
->vacancy_down
= td
->table_vacancy
.vacancy_down
;
733 otv
->vacancy_up
= td
->table_vacancy
.vacancy_up
;
734 otv
->vacancy
= td
->table_vacancy
.vacancy
;
737 otd
= ofpbuf_at_assert(reply
, start_otd
, sizeof *otd
);
738 otd
->length
= htons(reply
->size
- start_otd
);
739 otd
->table_id
= td
->table_id
;
740 otd
->config
= ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT
,
741 td
->eviction
, td
->vacancy
,
743 ofpmp_postappend(replies
, start_otd
);
747 ofputil_eviction_flag_to_string(uint32_t bit
)
749 enum ofp14_table_mod_prop_eviction_flag eviction_flag
= bit
;
751 switch (eviction_flag
) {
752 case OFPTMPEF14_OTHER
: return "OTHER";
753 case OFPTMPEF14_IMPORTANCE
: return "IMPORTANCE";
754 case OFPTMPEF14_LIFETIME
: return "LIFETIME";
760 /* Appends to 'string' a description of the bitmap of OFPTMPEF14_* values in
761 * 'eviction_flags'. */
763 ofputil_put_eviction_flags(struct ds
*string
, uint32_t eviction_flags
)
765 if (eviction_flags
!= UINT32_MAX
) {
766 ofp_print_bit_names(string
, eviction_flags
,
767 ofputil_eviction_flag_to_string
, '|');
769 ds_put_cstr(string
, "(default)");
774 ofputil_table_desc_format(struct ds
*s
, const struct ofputil_table_desc
*td
,
775 const struct ofputil_table_map
*table_map
)
777 ds_put_format(s
, "\n table ");
778 ofputil_format_table(td
->table_id
, table_map
, s
);
779 ds_put_cstr(s
, ":\n");
780 ds_put_format(s
, " eviction=%s eviction_flags=",
781 ofputil_table_eviction_to_string(td
->eviction
));
782 ofputil_put_eviction_flags(s
, td
->eviction_flags
);
783 ds_put_char(s
, '\n');
784 ds_put_format(s
, " vacancy=%s",
785 ofputil_table_vacancy_to_string(td
->vacancy
));
786 if (td
->vacancy
== OFPUTIL_TABLE_VACANCY_ON
) {
787 ds_put_format(s
, " vacancy_down=%"PRIu8
"%%",
788 td
->table_vacancy
.vacancy_down
);
789 ds_put_format(s
, " vacancy_up=%"PRIu8
"%%",
790 td
->table_vacancy
.vacancy_up
);
791 ds_put_format(s
, " vacancy=%"PRIu8
"%%",
792 td
->table_vacancy
.vacancy
);
794 ds_put_char(s
, '\n');
797 /* This function parses Vacancy property, and decodes the
798 * ofp14_table_mod_prop_vacancy in ofputil_table_mod.
799 * Returns OFPERR_OFPBPC_BAD_VALUE error code when vacancy_down is
800 * greater than vacancy_up and also when current vacancy has non-zero
801 * value. Returns 0 on success. */
803 parse_table_mod_vacancy_property(struct ofpbuf
*property
,
804 struct ofputil_table_mod
*tm
)
806 struct ofp14_table_mod_prop_vacancy
*otv
= property
->data
;
808 if (property
->size
!= sizeof *otv
) {
809 return OFPERR_OFPBPC_BAD_LEN
;
811 tm
->table_vacancy
.vacancy_down
= otv
->vacancy_down
;
812 tm
->table_vacancy
.vacancy_up
= otv
->vacancy_up
;
813 if (tm
->table_vacancy
.vacancy_down
> tm
->table_vacancy
.vacancy_up
) {
814 OFPPROP_LOG(&rl
, false,
815 "Value of vacancy_down is greater than vacancy_up");
816 return OFPERR_OFPBPC_BAD_VALUE
;
818 if (tm
->table_vacancy
.vacancy_down
> 100 ||
819 tm
->table_vacancy
.vacancy_up
> 100) {
820 OFPPROP_LOG(&rl
, false, "Vacancy threshold percentage "
821 "should not be greater than 100");
822 return OFPERR_OFPBPC_BAD_VALUE
;
824 tm
->table_vacancy
.vacancy
= otv
->vacancy
;
825 if (tm
->table_vacancy
.vacancy
) {
826 OFPPROP_LOG(&rl
, false,
827 "Vacancy value should be zero for table-mod messages");
828 return OFPERR_OFPBPC_BAD_VALUE
;
833 /* Given 'config', taken from an OpenFlow 'version' message that specifies
834 * table configuration (a table mod, table stats, or table features message),
835 * returns the table vacancy configuration that it specifies.
837 * Only OpenFlow 1.4 and later specify table vacancy configuration this way,
838 * so for other 'version' this function always returns
839 * OFPUTIL_TABLE_VACANCY_DEFAULT. */
840 static enum ofputil_table_vacancy
841 ofputil_decode_table_vacancy(ovs_be32 config
, enum ofp_version version
)
843 return (version
< OFP14_VERSION
? OFPUTIL_TABLE_VACANCY_DEFAULT
844 : config
& htonl(OFPTC14_VACANCY_EVENTS
) ? OFPUTIL_TABLE_VACANCY_ON
845 : OFPUTIL_TABLE_VACANCY_OFF
);
848 /* Given 'config', taken from an OpenFlow 'version' message that specifies
849 * table configuration (a table mod, table stats, or table features message),
850 * returns the table eviction configuration that it specifies.
852 * Only OpenFlow 1.4 and later specify table eviction configuration this way,
853 * so for other 'version' values this function always returns
854 * OFPUTIL_TABLE_EVICTION_DEFAULT. */
855 static enum ofputil_table_eviction
856 ofputil_decode_table_eviction(ovs_be32 config
, enum ofp_version version
)
858 return (version
< OFP14_VERSION
? OFPUTIL_TABLE_EVICTION_DEFAULT
859 : config
& htonl(OFPTC14_EVICTION
) ? OFPUTIL_TABLE_EVICTION_ON
860 : OFPUTIL_TABLE_EVICTION_OFF
);
863 /* Returns a bitmap of OFPTC* values suitable for 'config' fields in various
864 * OpenFlow messages of the given 'version', based on the provided 'miss' and
865 * 'eviction' values. */
867 ofputil_encode_table_config(enum ofputil_table_miss miss
,
868 enum ofputil_table_eviction eviction
,
869 enum ofputil_table_vacancy vacancy
,
870 enum ofp_version version
)
873 /* Search for "OFPTC_* Table Configuration" in the documentation for more
874 * information on the crazy evolution of this field. */
877 /* OpenFlow 1.0 didn't have such a field, any value ought to do. */
882 /* OpenFlow 1.1 and 1.2 define only OFPTC11_TABLE_MISS_*. */
884 case OFPUTIL_TABLE_MISS_DEFAULT
:
885 /* Really this shouldn't be used for encoding (the caller should
886 * provide a specific value) but I can't imagine that defaulting to
887 * the fall-through case here will hurt. */
888 case OFPUTIL_TABLE_MISS_CONTROLLER
:
890 return htonl(OFPTC11_TABLE_MISS_CONTROLLER
);
891 case OFPUTIL_TABLE_MISS_CONTINUE
:
892 return htonl(OFPTC11_TABLE_MISS_CONTINUE
);
893 case OFPUTIL_TABLE_MISS_DROP
:
894 return htonl(OFPTC11_TABLE_MISS_DROP
);
899 /* OpenFlow 1.3 removed OFPTC11_TABLE_MISS_* and didn't define any new
900 * flags, so this is correct. */
906 /* OpenFlow 1.4 introduced OFPTC14_EVICTION and
907 * OFPTC14_VACANCY_EVENTS. */
908 if (eviction
== OFPUTIL_TABLE_EVICTION_ON
) {
909 config
|= OFPTC14_EVICTION
;
911 if (vacancy
== OFPUTIL_TABLE_VACANCY_ON
) {
912 config
|= OFPTC14_VACANCY_EVENTS
;
914 return htonl(config
);
920 /* Given 'config', taken from an OpenFlow 'version' message that specifies
921 * table configuration (a table mod, table stats, or table features message),
922 * returns the table miss configuration that it specifies.
924 * Only OpenFlow 1.1 and 1.2 specify table miss configurations this way, so for
925 * other 'version' values this function always returns
926 * OFPUTIL_TABLE_MISS_DEFAULT. */
927 static enum ofputil_table_miss
928 ofputil_decode_table_miss(ovs_be32 config_
, enum ofp_version version
)
930 uint32_t config
= ntohl(config_
);
932 if (version
== OFP11_VERSION
|| version
== OFP12_VERSION
) {
933 switch (config
& OFPTC11_TABLE_MISS_MASK
) {
934 case OFPTC11_TABLE_MISS_CONTROLLER
:
935 return OFPUTIL_TABLE_MISS_CONTROLLER
;
937 case OFPTC11_TABLE_MISS_CONTINUE
:
938 return OFPUTIL_TABLE_MISS_CONTINUE
;
940 case OFPTC11_TABLE_MISS_DROP
:
941 return OFPUTIL_TABLE_MISS_DROP
;
944 VLOG_WARN_RL(&rl
, "bad table miss config %d", config
);
945 return OFPUTIL_TABLE_MISS_CONTROLLER
;
948 return OFPUTIL_TABLE_MISS_DEFAULT
;
952 /* Decodes the OpenFlow "table mod" message in '*oh' into an abstract form in
953 * '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */
955 ofputil_decode_table_mod(const struct ofp_header
*oh
,
956 struct ofputil_table_mod
*pm
)
958 memset(pm
, 0, sizeof *pm
);
959 pm
->miss
= OFPUTIL_TABLE_MISS_DEFAULT
;
960 pm
->eviction
= OFPUTIL_TABLE_EVICTION_DEFAULT
;
961 pm
->eviction_flags
= UINT32_MAX
;
962 pm
->vacancy
= OFPUTIL_TABLE_VACANCY_DEFAULT
;
964 struct ofpbuf b
= ofpbuf_const_initializer(oh
, ntohs(oh
->length
));
965 enum ofpraw raw
= ofpraw_pull_assert(&b
);
966 if (raw
== OFPRAW_OFPT11_TABLE_MOD
) {
967 const struct ofp11_table_mod
*otm
= b
.data
;
969 pm
->table_id
= otm
->table_id
;
970 pm
->miss
= ofputil_decode_table_miss(otm
->config
, oh
->version
);
971 } else if (raw
== OFPRAW_OFPT14_TABLE_MOD
) {
972 const struct ofp14_table_mod
*otm
= ofpbuf_pull(&b
, sizeof *otm
);
974 pm
->table_id
= otm
->table_id
;
975 pm
->miss
= ofputil_decode_table_miss(otm
->config
, oh
->version
);
976 pm
->eviction
= ofputil_decode_table_eviction(otm
->config
, oh
->version
);
977 pm
->vacancy
= ofputil_decode_table_vacancy(otm
->config
, oh
->version
);
979 struct ofpbuf property
;
983 error
= ofpprop_pull(&b
, &property
, &type
);
989 case OFPTMPT14_EVICTION
:
990 error
= ofpprop_parse_u32(&property
, &pm
->eviction
);
993 case OFPTMPT14_VACANCY
:
994 error
= parse_table_mod_vacancy_property(&property
, pm
);
998 error
= OFPERR_OFPBRC_BAD_TYPE
;
1007 return OFPERR_OFPBRC_BAD_TYPE
;
1013 /* Converts the abstract form of a "table mod" message in '*tm' into an
1014 * OpenFlow message suitable for 'protocol', and returns that encoded form in a
1015 * buffer owned by the caller. */
1017 ofputil_encode_table_mod(const struct ofputil_table_mod
*tm
,
1018 enum ofputil_protocol protocol
)
1020 enum ofp_version ofp_version
= ofputil_protocol_to_ofp_version(protocol
);
1023 switch (ofp_version
) {
1024 case OFP10_VERSION
: {
1025 ovs_fatal(0, "table mod needs OpenFlow 1.1 or later "
1026 "(\'-O OpenFlow11\')");
1031 case OFP13_VERSION
: {
1032 struct ofp11_table_mod
*otm
;
1034 b
= ofpraw_alloc(OFPRAW_OFPT11_TABLE_MOD
, ofp_version
, 0);
1035 otm
= ofpbuf_put_zeros(b
, sizeof *otm
);
1036 otm
->table_id
= tm
->table_id
;
1037 otm
->config
= ofputil_encode_table_config(tm
->miss
, tm
->eviction
,
1038 tm
->vacancy
, ofp_version
);
1043 case OFP16_VERSION
: {
1044 struct ofp14_table_mod
*otm
;
1046 b
= ofpraw_alloc(OFPRAW_OFPT14_TABLE_MOD
, ofp_version
, 0);
1047 otm
= ofpbuf_put_zeros(b
, sizeof *otm
);
1048 otm
->table_id
= tm
->table_id
;
1049 otm
->config
= ofputil_encode_table_config(tm
->miss
, tm
->eviction
,
1050 tm
->vacancy
, ofp_version
);
1052 if (tm
->eviction_flags
!= UINT32_MAX
) {
1053 ofpprop_put_u32(b
, OFPTMPT14_EVICTION
, tm
->eviction_flags
);
1055 if (tm
->vacancy
== OFPUTIL_TABLE_VACANCY_ON
) {
1056 struct ofp14_table_mod_prop_vacancy
*otv
;
1058 otv
= ofpprop_put_zeros(b
, OFPTMPT14_VACANCY
, sizeof *otv
);
1059 otv
->vacancy_down
= tm
->table_vacancy
.vacancy_down
;
1060 otv
->vacancy_up
= tm
->table_vacancy
.vacancy_up
;
1072 ofputil_table_mod_format(struct ds
*s
, const struct ofputil_table_mod
*tm
,
1073 const struct ofputil_table_map
*table_map
)
1075 if (tm
->table_id
== 0xff) {
1076 ds_put_cstr(s
, " table_id: ALL_TABLES");
1078 ds_put_format(s
, " table_id=");
1079 ofputil_format_table(tm
->table_id
, table_map
, s
);
1082 if (tm
->miss
!= OFPUTIL_TABLE_MISS_DEFAULT
) {
1083 ds_put_format(s
, ", flow_miss_config=%s",
1084 ofputil_table_miss_to_string(tm
->miss
));
1086 if (tm
->eviction
!= OFPUTIL_TABLE_EVICTION_DEFAULT
) {
1087 ds_put_format(s
, ", eviction=%s",
1088 ofputil_table_eviction_to_string(tm
->eviction
));
1090 if (tm
->eviction_flags
!= UINT32_MAX
) {
1091 ds_put_cstr(s
, "eviction_flags=");
1092 ofputil_put_eviction_flags(s
, tm
->eviction_flags
);
1094 if (tm
->vacancy
!= OFPUTIL_TABLE_VACANCY_DEFAULT
) {
1095 ds_put_format(s
, ", vacancy=%s",
1096 ofputil_table_vacancy_to_string(tm
->vacancy
));
1097 if (tm
->vacancy
== OFPUTIL_TABLE_VACANCY_ON
) {
1098 ds_put_format(s
, " vacancy:%"PRIu8
""
1099 ",%"PRIu8
"", tm
->table_vacancy
.vacancy_down
,
1100 tm
->table_vacancy
.vacancy_up
);
1105 /* Convert 'setting' (as described for the "mod-table" command
1106 * in ovs-ofctl man page) into 'tm->table_vacancy->vacancy_up' and
1107 * 'tm->table_vacancy->vacancy_down' threshold values.
1108 * For the two threshold values, value of vacancy_up is always greater
1109 * than value of vacancy_down.
1111 * Returns NULL if successful, otherwise a malloc()'d string describing the
1112 * error. The caller is responsible for freeing the returned string. */
1113 static char * OVS_WARN_UNUSED_RESULT
1114 parse_ofp_table_vacancy(struct ofputil_table_mod
*tm
, const char *setting
)
1116 char *save_ptr
= NULL
;
1117 char *vac_up
, *vac_down
;
1118 char *value
= xstrdup(setting
);
1120 int vacancy_up
, vacancy_down
;
1122 strtok_r(value
, ":", &save_ptr
);
1123 vac_down
= strtok_r(NULL
, ",", &save_ptr
);
1125 ret_msg
= xasprintf("Vacancy down value missing");
1128 if (!str_to_int(vac_down
, 0, &vacancy_down
) ||
1129 vacancy_down
< 0 || vacancy_down
> 100) {
1130 ret_msg
= xasprintf("Invalid vacancy down value \"%s\"", vac_down
);
1133 vac_up
= strtok_r(NULL
, ",", &save_ptr
);
1135 ret_msg
= xasprintf("Vacancy up value missing");
1138 if (!str_to_int(vac_up
, 0, &vacancy_up
) ||
1139 vacancy_up
< 0 || vacancy_up
> 100) {
1140 ret_msg
= xasprintf("Invalid vacancy up value \"%s\"", vac_up
);
1143 if (vacancy_down
> vacancy_up
) {
1144 ret_msg
= xasprintf("Invalid vacancy range, vacancy up should be "
1145 "greater than vacancy down (%s)",
1146 ofperr_to_string(OFPERR_OFPBPC_BAD_VALUE
));
1151 tm
->table_vacancy
.vacancy_down
= vacancy_down
;
1152 tm
->table_vacancy
.vacancy_up
= vacancy_up
;
1160 /* Convert 'table_id' and 'setting' (as described for the "mod-table" command
1161 * in the ovs-ofctl man page) into 'tm' for sending a table_mod command to a
1164 * Stores a bitmap of the OpenFlow versions that are usable for 'tm' into
1165 * '*usable_versions'.
1167 * Returns NULL if successful, otherwise a malloc()'d string describing the
1168 * error. The caller is responsible for freeing the returned string. */
1169 char * OVS_WARN_UNUSED_RESULT
1170 parse_ofp_table_mod(struct ofputil_table_mod
*tm
, const char *table_id
,
1171 const char *setting
,
1172 const struct ofputil_table_map
*table_map
,
1173 uint32_t *usable_versions
)
1175 *usable_versions
= 0;
1176 if (!strcasecmp(table_id
, "all")) {
1177 tm
->table_id
= OFPTT_ALL
;
1178 } else if (!ofputil_table_from_string(table_id
, table_map
,
1180 return xasprintf("unknown table \"%s\"", table_id
);
1183 tm
->miss
= OFPUTIL_TABLE_MISS_DEFAULT
;
1184 tm
->eviction
= OFPUTIL_TABLE_EVICTION_DEFAULT
;
1185 tm
->eviction_flags
= UINT32_MAX
;
1186 tm
->vacancy
= OFPUTIL_TABLE_VACANCY_DEFAULT
;
1187 tm
->table_vacancy
.vacancy_down
= 0;
1188 tm
->table_vacancy
.vacancy_up
= 0;
1189 tm
->table_vacancy
.vacancy
= 0;
1190 /* Only OpenFlow 1.1 and 1.2 can configure table-miss via table_mod.
1191 * Only OpenFlow 1.4+ can configure eviction and vacancy events
1194 if (!strcmp(setting
, "controller")) {
1195 tm
->miss
= OFPUTIL_TABLE_MISS_CONTROLLER
;
1196 *usable_versions
= (1u << OFP11_VERSION
) | (1u << OFP12_VERSION
);
1197 } else if (!strcmp(setting
, "continue")) {
1198 tm
->miss
= OFPUTIL_TABLE_MISS_CONTINUE
;
1199 *usable_versions
= (1u << OFP11_VERSION
) | (1u << OFP12_VERSION
);
1200 } else if (!strcmp(setting
, "drop")) {
1201 tm
->miss
= OFPUTIL_TABLE_MISS_DROP
;
1202 *usable_versions
= (1u << OFP11_VERSION
) | (1u << OFP12_VERSION
);
1203 } else if (!strcmp(setting
, "evict")) {
1204 tm
->eviction
= OFPUTIL_TABLE_EVICTION_ON
;
1205 *usable_versions
= (1 << OFP14_VERSION
) | (1u << OFP15_VERSION
);
1206 } else if (!strcmp(setting
, "noevict")) {
1207 tm
->eviction
= OFPUTIL_TABLE_EVICTION_OFF
;
1208 *usable_versions
= (1 << OFP14_VERSION
) | (1u << OFP15_VERSION
);
1209 } else if (!strncmp(setting
, "vacancy", strcspn(setting
, ":"))) {
1210 tm
->vacancy
= OFPUTIL_TABLE_VACANCY_ON
;
1211 *usable_versions
= (1 << OFP14_VERSION
) | (1u << OFP15_VERSION
);
1212 char *error
= parse_ofp_table_vacancy(tm
, setting
);
1216 } else if (!strcmp(setting
, "novacancy")) {
1217 tm
->vacancy
= OFPUTIL_TABLE_VACANCY_OFF
;
1218 *usable_versions
= (1 << OFP14_VERSION
) | (1u << OFP15_VERSION
);
1220 return xasprintf("invalid table_mod setting %s", setting
);
1223 if (tm
->table_id
== 0xfe
1224 && tm
->miss
== OFPUTIL_TABLE_MISS_CONTINUE
) {
1225 return xstrdup("last table's flow miss handling can not be continue");
1232 print_table_action_features(struct ds
*s
,
1233 const struct ofputil_table_action_features
*taf
)
1236 ds_put_cstr(s
, " actions: ");
1237 ofpact_bitmap_format(taf
->ofpacts
, s
);
1238 ds_put_char(s
, '\n');
1241 if (!bitmap_is_all_zeros(taf
->set_fields
.bm
, MFF_N_IDS
)) {
1244 ds_put_cstr(s
, " supported on Set-Field:");
1245 BITMAP_FOR_EACH_1 (i
, MFF_N_IDS
, taf
->set_fields
.bm
) {
1246 ds_put_format(s
, " %s", mf_from_id(i
)->name
);
1248 ds_put_char(s
, '\n');
1253 table_action_features_equal(const struct ofputil_table_action_features
*a
,
1254 const struct ofputil_table_action_features
*b
)
1256 return (a
->ofpacts
== b
->ofpacts
1257 && bitmap_equal(a
->set_fields
.bm
, b
->set_fields
.bm
, MFF_N_IDS
));
1261 table_action_features_empty(const struct ofputil_table_action_features
*taf
)
1263 return !taf
->ofpacts
&& bitmap_is_all_zeros(taf
->set_fields
.bm
, MFF_N_IDS
);
1267 print_table_instruction_features(
1269 const struct ofputil_table_instruction_features
*tif
,
1270 const struct ofputil_table_instruction_features
*prev_tif
)
1274 if (!bitmap_is_all_zeros(tif
->next
, 255)) {
1275 ds_put_cstr(s
, " next tables: ");
1276 for (start
= bitmap_scan(tif
->next
, 1, 0, 255); start
< 255;
1277 start
= bitmap_scan(tif
->next
, 1, end
, 255)) {
1278 end
= bitmap_scan(tif
->next
, 0, start
+ 1, 255);
1279 if (end
== start
+ 1) {
1280 ds_put_format(s
, "%d,", start
);
1282 ds_put_format(s
, "%d-%d,", start
, end
- 1);
1286 if (ds_last(s
) == ' ') {
1287 ds_put_cstr(s
, "none");
1289 ds_put_char(s
, '\n');
1292 if (tif
->instructions
) {
1293 if (prev_tif
&& tif
->instructions
== prev_tif
->instructions
) {
1294 ds_put_cstr(s
, " (same instructions)\n");
1296 ds_put_cstr(s
, " instructions: ");
1299 for (i
= 0; i
< 32; i
++) {
1300 if (tif
->instructions
& (1u << i
)) {
1301 const char *name
= ovs_instruction_name_from_type(i
);
1303 ds_put_cstr(s
, name
);
1305 ds_put_format(s
, "%d", i
);
1307 ds_put_char(s
, ',');
1311 ds_put_char(s
, '\n');
1316 && table_action_features_equal(&tif
->write
, &prev_tif
->write
)
1317 && table_action_features_equal(&tif
->apply
, &prev_tif
->apply
)
1318 && !bitmap_is_all_zeros(tif
->write
.set_fields
.bm
, MFF_N_IDS
)) {
1319 ds_put_cstr(s
, " (same actions)\n");
1320 } else if (!table_action_features_equal(&tif
->write
, &tif
->apply
)) {
1321 ds_put_cstr(s
, " Write-Actions features:\n");
1322 print_table_action_features(s
, &tif
->write
);
1323 ds_put_cstr(s
, " Apply-Actions features:\n");
1324 print_table_action_features(s
, &tif
->apply
);
1325 } else if (tif
->write
.ofpacts
1326 || !bitmap_is_all_zeros(tif
->write
.set_fields
.bm
, MFF_N_IDS
)) {
1327 ds_put_cstr(s
, " Write-Actions and Apply-Actions features:\n");
1328 print_table_action_features(s
, &tif
->write
);
1333 table_instruction_features_equal(
1334 const struct ofputil_table_instruction_features
*a
,
1335 const struct ofputil_table_instruction_features
*b
)
1337 return (bitmap_equal(a
->next
, b
->next
, 255)
1338 && a
->instructions
== b
->instructions
1339 && table_action_features_equal(&a
->write
, &b
->write
)
1340 && table_action_features_equal(&a
->apply
, &b
->apply
));
1344 table_instruction_features_empty(
1345 const struct ofputil_table_instruction_features
*tif
)
1347 return (bitmap_is_all_zeros(tif
->next
, 255)
1348 && !tif
->instructions
1349 && table_action_features_empty(&tif
->write
)
1350 && table_action_features_empty(&tif
->apply
));
1354 table_features_equal(const struct ofputil_table_features
*a
,
1355 const struct ofputil_table_features
*b
)
1357 return (a
->metadata_match
== b
->metadata_match
1358 && a
->metadata_write
== b
->metadata_write
1359 && a
->miss_config
== b
->miss_config
1360 && a
->supports_eviction
== b
->supports_eviction
1361 && a
->supports_vacancy_events
== b
->supports_vacancy_events
1362 && a
->max_entries
== b
->max_entries
1363 && table_instruction_features_equal(&a
->nonmiss
, &b
->nonmiss
)
1364 && table_instruction_features_equal(&a
->miss
, &b
->miss
)
1365 && bitmap_equal(a
->match
.bm
, b
->match
.bm
, MFF_N_IDS
));
1369 table_features_empty(const struct ofputil_table_features
*tf
)
1371 return (!tf
->metadata_match
1372 && !tf
->metadata_write
1373 && tf
->miss_config
== OFPUTIL_TABLE_MISS_DEFAULT
1374 && tf
->supports_eviction
< 0
1375 && tf
->supports_vacancy_events
< 0
1377 && table_instruction_features_empty(&tf
->nonmiss
)
1378 && table_instruction_features_empty(&tf
->miss
)
1379 && bitmap_is_all_zeros(tf
->match
.bm
, MFF_N_IDS
));
1383 table_stats_equal(const struct ofputil_table_stats
*a
,
1384 const struct ofputil_table_stats
*b
)
1386 return (a
->active_count
== b
->active_count
1387 && a
->lookup_count
== b
->lookup_count
1388 && a
->matched_count
== b
->matched_count
);
1392 ofputil_table_features_format(
1394 const struct ofputil_table_features
*features
,
1395 const struct ofputil_table_features
*prev_features
,
1396 const struct ofputil_table_stats
*stats
,
1397 const struct ofputil_table_stats
*prev_stats
,
1398 const struct ofputil_table_map
*table_map
)
1402 ds_put_format(s
, " table ");
1403 ofputil_format_table(features
->table_id
, table_map
, s
);
1404 if (features
->name
[0]) {
1405 ds_put_format(s
, " (\"%s\")", features
->name
);
1407 ds_put_char(s
, ':');
1409 bool same_stats
= prev_stats
&& table_stats_equal(stats
, prev_stats
);
1410 bool same_features
= prev_features
&& table_features_equal(features
,
1412 if ((!stats
|| same_stats
) && same_features
) {
1413 ds_put_cstr(s
, " ditto");
1416 ds_put_char(s
, '\n');
1418 ds_put_format(s
, " active=%"PRIu32
", ", stats
->active_count
);
1419 ds_put_format(s
, "lookup=%"PRIu64
", ", stats
->lookup_count
);
1420 ds_put_format(s
, "matched=%"PRIu64
"\n", stats
->matched_count
);
1422 if (same_features
) {
1423 if (!table_features_empty(features
)) {
1424 ds_put_cstr(s
, " (same features)\n");
1428 if (features
->metadata_match
|| features
->metadata_write
) {
1429 ds_put_format(s
, " metadata: match=%#"PRIx64
" write=%#"PRIx64
"\n",
1430 ntohll(features
->metadata_match
),
1431 ntohll(features
->metadata_write
));
1434 if (features
->miss_config
!= OFPUTIL_TABLE_MISS_DEFAULT
) {
1435 ds_put_format(s
, " config=%s\n",
1436 ofputil_table_miss_to_string(features
->miss_config
));
1439 if (features
->supports_eviction
>= 0) {
1440 ds_put_format(s
, " eviction: %ssupported\n",
1441 features
->supports_eviction
? "" : "not ");
1444 if (features
->supports_vacancy_events
>= 0) {
1445 ds_put_format(s
, " vacancy events: %ssupported\n",
1446 features
->supports_vacancy_events
? "" : "not ");
1450 if (features
->max_entries
) {
1451 ds_put_format(s
, " max_entries=%"PRIu32
"\n", features
->max_entries
);
1454 const struct ofputil_table_instruction_features
*prev_nonmiss
1455 = prev_features
? &prev_features
->nonmiss
: NULL
;
1456 const struct ofputil_table_instruction_features
*prev_miss
1457 = prev_features
? &prev_features
->miss
: NULL
;
1459 && table_instruction_features_equal(&features
->nonmiss
, prev_nonmiss
)
1460 && table_instruction_features_equal(&features
->miss
, prev_miss
)) {
1461 if (!table_instruction_features_empty(&features
->nonmiss
)) {
1462 ds_put_cstr(s
, " (same instructions)\n");
1464 } else if (!table_instruction_features_equal(&features
->nonmiss
,
1466 ds_put_cstr(s
, " instructions (other than table miss):\n");
1467 print_table_instruction_features(s
, &features
->nonmiss
, prev_nonmiss
);
1468 ds_put_cstr(s
, " instructions (table miss):\n");
1469 print_table_instruction_features(s
, &features
->miss
, prev_miss
);
1470 } else if (!table_instruction_features_empty(&features
->nonmiss
)) {
1471 ds_put_cstr(s
, " instructions (table miss and others):\n");
1472 print_table_instruction_features(s
, &features
->nonmiss
, prev_nonmiss
);
1475 if (!bitmap_is_all_zeros(features
->match
.bm
, MFF_N_IDS
)) {
1477 && bitmap_equal(features
->match
.bm
, prev_features
->match
.bm
,
1479 ds_put_cstr(s
, " (same matching)\n");
1481 ds_put_cstr(s
, " matching:\n");
1482 BITMAP_FOR_EACH_1 (i
, MFF_N_IDS
, features
->match
.bm
) {
1483 const struct mf_field
*f
= mf_from_id(i
);
1484 bool mask
= bitmap_is_set(features
->mask
.bm
, i
);
1485 bool wildcard
= bitmap_is_set(features
->wildcard
.bm
, i
);
1487 ds_put_format(s
, " %s: %s\n",
1489 (mask
? "arbitrary mask"
1490 : wildcard
? "exact match or wildcard"
1491 : "must exact match"));
1499 /* OpenFlow 1.0 and 1.1 don't distinguish between a field that cannot be
1500 * matched and a field that must be wildcarded. This function returns a bitmap
1501 * that contains both kinds of fields. */
1502 static struct mf_bitmap
1503 wild_or_nonmatchable_fields(const struct ofputil_table_features
*features
)
1505 struct mf_bitmap wc
= features
->match
;
1506 bitmap_not(wc
.bm
, MFF_N_IDS
);
1507 bitmap_or(wc
.bm
, features
->wildcard
.bm
, MFF_N_IDS
);
1511 struct ofp10_wc_map
{
1512 enum ofp10_flow_wildcards wc10
;
1513 enum mf_field_id mf
;
1516 static const struct ofp10_wc_map ofp10_wc_map
[] = {
1517 { OFPFW10_IN_PORT
, MFF_IN_PORT
},
1518 { OFPFW10_DL_VLAN
, MFF_VLAN_VID
},
1519 { OFPFW10_DL_SRC
, MFF_ETH_SRC
},
1520 { OFPFW10_DL_DST
, MFF_ETH_DST
},
1521 { OFPFW10_DL_TYPE
, MFF_ETH_TYPE
},
1522 { OFPFW10_NW_PROTO
, MFF_IP_PROTO
},
1523 { OFPFW10_TP_SRC
, MFF_TCP_SRC
},
1524 { OFPFW10_TP_DST
, MFF_TCP_DST
},
1525 { OFPFW10_NW_SRC_MASK
, MFF_IPV4_SRC
},
1526 { OFPFW10_NW_DST_MASK
, MFF_IPV4_DST
},
1527 { OFPFW10_DL_VLAN_PCP
, MFF_VLAN_PCP
},
1528 { OFPFW10_NW_TOS
, MFF_IP_DSCP
},
1532 mf_bitmap_to_of10(const struct mf_bitmap
*fields
)
1534 const struct ofp10_wc_map
*p
;
1537 for (p
= ofp10_wc_map
; p
< &ofp10_wc_map
[ARRAY_SIZE(ofp10_wc_map
)]; p
++) {
1538 if (bitmap_is_set(fields
->bm
, p
->mf
)) {
1545 static struct mf_bitmap
1546 mf_bitmap_from_of10(ovs_be32 wc10_
)
1548 struct mf_bitmap fields
= MF_BITMAP_INITIALIZER
;
1549 const struct ofp10_wc_map
*p
;
1550 uint32_t wc10
= ntohl(wc10_
);
1552 for (p
= ofp10_wc_map
; p
< &ofp10_wc_map
[ARRAY_SIZE(ofp10_wc_map
)]; p
++) {
1553 if (wc10
& p
->wc10
) {
1554 bitmap_set1(fields
.bm
, p
->mf
);
1561 ofputil_put_ofp10_table_stats(const struct ofputil_table_stats
*stats
,
1562 const struct ofputil_table_features
*features
,
1565 struct mf_bitmap wc
= wild_or_nonmatchable_fields(features
);
1566 struct ofp10_table_stats
*out
;
1568 out
= ofpbuf_put_zeros(buf
, sizeof *out
);
1569 out
->table_id
= features
->table_id
;
1570 ovs_strlcpy_arrays(out
->name
, features
->name
);
1571 out
->wildcards
= mf_bitmap_to_of10(&wc
);
1572 out
->max_entries
= htonl(features
->max_entries
);
1573 out
->active_count
= htonl(stats
->active_count
);
1574 put_32aligned_be64(&out
->lookup_count
, htonll(stats
->lookup_count
));
1575 put_32aligned_be64(&out
->matched_count
, htonll(stats
->matched_count
));
1578 struct ofp11_wc_map
{
1579 enum ofp11_flow_match_fields wc11
;
1580 enum mf_field_id mf
;
1583 static const struct ofp11_wc_map ofp11_wc_map
[] = {
1584 { OFPFMF11_IN_PORT
, MFF_IN_PORT
},
1585 { OFPFMF11_DL_VLAN
, MFF_VLAN_VID
},
1586 { OFPFMF11_DL_VLAN_PCP
, MFF_VLAN_PCP
},
1587 { OFPFMF11_DL_TYPE
, MFF_ETH_TYPE
},
1588 { OFPFMF11_NW_TOS
, MFF_IP_DSCP
},
1589 { OFPFMF11_NW_PROTO
, MFF_IP_PROTO
},
1590 { OFPFMF11_TP_SRC
, MFF_TCP_SRC
},
1591 { OFPFMF11_TP_DST
, MFF_TCP_DST
},
1592 { OFPFMF11_MPLS_LABEL
, MFF_MPLS_LABEL
},
1593 { OFPFMF11_MPLS_TC
, MFF_MPLS_TC
},
1594 /* I don't know what OFPFMF11_TYPE means. */
1595 { OFPFMF11_DL_SRC
, MFF_ETH_SRC
},
1596 { OFPFMF11_DL_DST
, MFF_ETH_DST
},
1597 { OFPFMF11_NW_SRC
, MFF_IPV4_SRC
},
1598 { OFPFMF11_NW_DST
, MFF_IPV4_DST
},
1599 { OFPFMF11_METADATA
, MFF_METADATA
},
1603 mf_bitmap_to_of11(const struct mf_bitmap
*fields
)
1605 const struct ofp11_wc_map
*p
;
1608 for (p
= ofp11_wc_map
; p
< &ofp11_wc_map
[ARRAY_SIZE(ofp11_wc_map
)]; p
++) {
1609 if (bitmap_is_set(fields
->bm
, p
->mf
)) {
1616 static struct mf_bitmap
1617 mf_bitmap_from_of11(ovs_be32 wc11_
)
1619 struct mf_bitmap fields
= MF_BITMAP_INITIALIZER
;
1620 const struct ofp11_wc_map
*p
;
1621 uint32_t wc11
= ntohl(wc11_
);
1623 for (p
= ofp11_wc_map
; p
< &ofp11_wc_map
[ARRAY_SIZE(ofp11_wc_map
)]; p
++) {
1624 if (wc11
& p
->wc11
) {
1625 bitmap_set1(fields
.bm
, p
->mf
);
1632 ofputil_put_ofp11_table_stats(const struct ofputil_table_stats
*stats
,
1633 const struct ofputil_table_features
*features
,
1636 struct mf_bitmap wc
= wild_or_nonmatchable_fields(features
);
1637 struct ofp11_table_stats
*out
;
1639 out
= ofpbuf_put_zeros(buf
, sizeof *out
);
1640 out
->table_id
= features
->table_id
;
1641 ovs_strlcpy_arrays(out
->name
, features
->name
);
1642 out
->wildcards
= mf_bitmap_to_of11(&wc
);
1643 out
->match
= mf_bitmap_to_of11(&features
->match
);
1644 out
->instructions
= ovsinst_bitmap_to_openflow(
1645 features
->nonmiss
.instructions
, OFP11_VERSION
);
1646 out
->write_actions
= ofpact_bitmap_to_openflow(
1647 features
->nonmiss
.write
.ofpacts
, OFP11_VERSION
);
1648 out
->apply_actions
= ofpact_bitmap_to_openflow(
1649 features
->nonmiss
.apply
.ofpacts
, OFP11_VERSION
);
1650 out
->config
= htonl(features
->miss_config
);
1651 out
->max_entries
= htonl(features
->max_entries
);
1652 out
->active_count
= htonl(stats
->active_count
);
1653 out
->lookup_count
= htonll(stats
->lookup_count
);
1654 out
->matched_count
= htonll(stats
->matched_count
);
1658 ofputil_put_ofp12_table_stats(const struct ofputil_table_stats
*stats
,
1659 const struct ofputil_table_features
*features
,
1662 struct ofp12_table_stats
*out
;
1664 out
= ofpbuf_put_zeros(buf
, sizeof *out
);
1665 out
->table_id
= features
->table_id
;
1666 ovs_strlcpy_arrays(out
->name
, features
->name
);
1667 out
->match
= oxm_bitmap_from_mf_bitmap(&features
->match
, OFP12_VERSION
);
1668 out
->wildcards
= oxm_bitmap_from_mf_bitmap(&features
->wildcard
,
1670 out
->write_actions
= ofpact_bitmap_to_openflow(
1671 features
->nonmiss
.write
.ofpacts
, OFP12_VERSION
);
1672 out
->apply_actions
= ofpact_bitmap_to_openflow(
1673 features
->nonmiss
.apply
.ofpacts
, OFP12_VERSION
);
1674 out
->write_setfields
= oxm_bitmap_from_mf_bitmap(
1675 &features
->nonmiss
.write
.set_fields
, OFP12_VERSION
);
1676 out
->apply_setfields
= oxm_bitmap_from_mf_bitmap(
1677 &features
->nonmiss
.apply
.set_fields
, OFP12_VERSION
);
1678 out
->metadata_match
= features
->metadata_match
;
1679 out
->metadata_write
= features
->metadata_write
;
1680 out
->instructions
= ovsinst_bitmap_to_openflow(
1681 features
->nonmiss
.instructions
, OFP12_VERSION
);
1682 out
->config
= ofputil_encode_table_config(features
->miss_config
,
1683 OFPUTIL_TABLE_EVICTION_DEFAULT
,
1684 OFPUTIL_TABLE_VACANCY_DEFAULT
,
1686 out
->max_entries
= htonl(features
->max_entries
);
1687 out
->active_count
= htonl(stats
->active_count
);
1688 out
->lookup_count
= htonll(stats
->lookup_count
);
1689 out
->matched_count
= htonll(stats
->matched_count
);
1693 ofputil_put_ofp13_table_stats(const struct ofputil_table_stats
*stats
,
1696 struct ofp13_table_stats
*out
;
1698 out
= ofpbuf_put_zeros(buf
, sizeof *out
);
1699 out
->table_id
= stats
->table_id
;
1700 out
->active_count
= htonl(stats
->active_count
);
1701 out
->lookup_count
= htonll(stats
->lookup_count
);
1702 out
->matched_count
= htonll(stats
->matched_count
);
1706 ofputil_encode_table_stats_reply(const struct ofp_header
*request
)
1708 return ofpraw_alloc_stats_reply(request
, 0);
1712 ofputil_append_table_stats_reply(struct ofpbuf
*reply
,
1713 const struct ofputil_table_stats
*stats
,
1714 const struct ofputil_table_features
*features
)
1716 struct ofp_header
*oh
= reply
->header
;
1718 ovs_assert(stats
->table_id
== features
->table_id
);
1720 switch ((enum ofp_version
) oh
->version
) {
1722 ofputil_put_ofp10_table_stats(stats
, features
, reply
);
1726 ofputil_put_ofp11_table_stats(stats
, features
, reply
);
1730 ofputil_put_ofp12_table_stats(stats
, features
, reply
);
1737 ofputil_put_ofp13_table_stats(stats
, reply
);
1746 ofputil_decode_ofp10_table_stats(struct ofpbuf
*msg
,
1747 struct ofputil_table_stats
*stats
,
1748 struct ofputil_table_features
*features
)
1750 struct ofp10_table_stats
*ots
;
1752 ots
= ofpbuf_try_pull(msg
, sizeof *ots
);
1754 return OFPERR_OFPBRC_BAD_LEN
;
1757 features
->table_id
= ots
->table_id
;
1758 ovs_strlcpy_arrays(features
->name
, ots
->name
);
1759 features
->max_entries
= ntohl(ots
->max_entries
);
1760 features
->match
= features
->wildcard
= mf_bitmap_from_of10(ots
->wildcards
);
1762 stats
->table_id
= ots
->table_id
;
1763 stats
->active_count
= ntohl(ots
->active_count
);
1764 stats
->lookup_count
= ntohll(get_32aligned_be64(&ots
->lookup_count
));
1765 stats
->matched_count
= ntohll(get_32aligned_be64(&ots
->matched_count
));
1771 ofputil_decode_ofp11_table_stats(struct ofpbuf
*msg
,
1772 struct ofputil_table_stats
*stats
,
1773 struct ofputil_table_features
*features
)
1775 struct ofp11_table_stats
*ots
;
1777 ots
= ofpbuf_try_pull(msg
, sizeof *ots
);
1779 return OFPERR_OFPBRC_BAD_LEN
;
1782 features
->table_id
= ots
->table_id
;
1783 ovs_strlcpy_arrays(features
->name
, ots
->name
);
1784 features
->max_entries
= ntohl(ots
->max_entries
);
1785 features
->nonmiss
.instructions
= ovsinst_bitmap_from_openflow(
1786 ots
->instructions
, OFP11_VERSION
);
1787 features
->nonmiss
.write
.ofpacts
= ofpact_bitmap_from_openflow(
1788 ots
->write_actions
, OFP11_VERSION
);
1789 features
->nonmiss
.apply
.ofpacts
= ofpact_bitmap_from_openflow(
1790 ots
->write_actions
, OFP11_VERSION
);
1791 features
->miss
= features
->nonmiss
;
1792 features
->miss_config
= ofputil_decode_table_miss(ots
->config
,
1794 features
->match
= mf_bitmap_from_of11(ots
->match
);
1795 features
->wildcard
= mf_bitmap_from_of11(ots
->wildcards
);
1796 bitmap_or(features
->match
.bm
, features
->wildcard
.bm
, MFF_N_IDS
);
1798 stats
->table_id
= ots
->table_id
;
1799 stats
->active_count
= ntohl(ots
->active_count
);
1800 stats
->lookup_count
= ntohll(ots
->lookup_count
);
1801 stats
->matched_count
= ntohll(ots
->matched_count
);
1807 ofputil_decode_ofp12_table_stats(struct ofpbuf
*msg
,
1808 struct ofputil_table_stats
*stats
,
1809 struct ofputil_table_features
*features
)
1811 struct ofp12_table_stats
*ots
;
1813 ots
= ofpbuf_try_pull(msg
, sizeof *ots
);
1815 return OFPERR_OFPBRC_BAD_LEN
;
1818 features
->table_id
= ots
->table_id
;
1819 ovs_strlcpy_arrays(features
->name
, ots
->name
);
1820 features
->metadata_match
= ots
->metadata_match
;
1821 features
->metadata_write
= ots
->metadata_write
;
1822 features
->miss_config
= ofputil_decode_table_miss(ots
->config
,
1824 features
->max_entries
= ntohl(ots
->max_entries
);
1826 features
->nonmiss
.instructions
= ovsinst_bitmap_from_openflow(
1827 ots
->instructions
, OFP12_VERSION
);
1828 features
->nonmiss
.write
.ofpacts
= ofpact_bitmap_from_openflow(
1829 ots
->write_actions
, OFP12_VERSION
);
1830 features
->nonmiss
.apply
.ofpacts
= ofpact_bitmap_from_openflow(
1831 ots
->apply_actions
, OFP12_VERSION
);
1832 features
->nonmiss
.write
.set_fields
= oxm_bitmap_to_mf_bitmap(
1833 ots
->write_setfields
, OFP12_VERSION
);
1834 features
->nonmiss
.apply
.set_fields
= oxm_bitmap_to_mf_bitmap(
1835 ots
->apply_setfields
, OFP12_VERSION
);
1836 features
->miss
= features
->nonmiss
;
1838 features
->match
= oxm_bitmap_to_mf_bitmap(ots
->match
, OFP12_VERSION
);
1839 features
->wildcard
= oxm_bitmap_to_mf_bitmap(ots
->wildcards
,
1841 bitmap_or(features
->match
.bm
, features
->wildcard
.bm
, MFF_N_IDS
);
1843 stats
->table_id
= ots
->table_id
;
1844 stats
->active_count
= ntohl(ots
->active_count
);
1845 stats
->lookup_count
= ntohll(ots
->lookup_count
);
1846 stats
->matched_count
= ntohll(ots
->matched_count
);
1852 ofputil_decode_ofp13_table_stats(struct ofpbuf
*msg
,
1853 struct ofputil_table_stats
*stats
,
1854 struct ofputil_table_features
*features
)
1856 struct ofp13_table_stats
*ots
;
1858 ots
= ofpbuf_try_pull(msg
, sizeof *ots
);
1860 return OFPERR_OFPBRC_BAD_LEN
;
1863 features
->table_id
= ots
->table_id
;
1865 stats
->table_id
= ots
->table_id
;
1866 stats
->active_count
= ntohl(ots
->active_count
);
1867 stats
->lookup_count
= ntohll(ots
->lookup_count
);
1868 stats
->matched_count
= ntohll(ots
->matched_count
);
1874 ofputil_decode_table_stats_reply(struct ofpbuf
*msg
,
1875 struct ofputil_table_stats
*stats
,
1876 struct ofputil_table_features
*features
)
1878 const struct ofp_header
*oh
;
1881 ofpraw_pull_assert(msg
);
1889 memset(stats
, 0, sizeof *stats
);
1890 memset(features
, 0, sizeof *features
);
1891 features
->supports_eviction
= -1;
1892 features
->supports_vacancy_events
= -1;
1894 switch ((enum ofp_version
) oh
->version
) {
1896 return ofputil_decode_ofp10_table_stats(msg
, stats
, features
);
1899 return ofputil_decode_ofp11_table_stats(msg
, stats
, features
);
1902 return ofputil_decode_ofp12_table_stats(msg
, stats
, features
);
1908 return ofputil_decode_ofp13_table_stats(msg
, stats
, features
);
1915 /* Returns a string form of 'reason'. The return value is either a statically
1916 * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'.
1917 * 'bufsize' should be at least OFP_ASYNC_CONFIG_REASON_BUFSIZE. */
1919 ofp_table_reason_to_string(enum ofp14_table_reason reason
,
1920 char *reasonbuf
, size_t bufsize
)
1923 case OFPTR_VACANCY_DOWN
:
1924 return "vacancy_down";
1926 case OFPTR_VACANCY_UP
:
1927 return "vacancy_up";
1930 snprintf(reasonbuf
, bufsize
, "%d", (int) reason
);
1936 ofputil_put_ofp14_table_desc(const struct ofputil_table_desc
*td
,
1937 struct ofpbuf
*b
, enum ofp_version version
)
1939 struct ofp14_table_desc
*otd
;
1940 struct ofp14_table_mod_prop_vacancy
*otv
;
1943 start_otd
= b
->size
;
1944 ofpbuf_put_zeros(b
, sizeof *otd
);
1946 ofpprop_put_u32(b
, OFPTMPT14_EVICTION
, td
->eviction_flags
);
1948 otv
= ofpbuf_put_zeros(b
, sizeof *otv
);
1949 otv
->type
= htons(OFPTMPT14_VACANCY
);
1950 otv
->length
= htons(sizeof *otv
);
1951 otv
->vacancy_down
= td
->table_vacancy
.vacancy_down
;
1952 otv
->vacancy_up
= td
->table_vacancy
.vacancy_up
;
1953 otv
->vacancy
= td
->table_vacancy
.vacancy
;
1955 otd
= ofpbuf_at_assert(b
, start_otd
, sizeof *otd
);
1956 otd
->length
= htons(b
->size
- start_otd
);
1957 otd
->table_id
= td
->table_id
;
1958 otd
->config
= ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT
,
1959 td
->eviction
, td
->vacancy
,
1963 /* Converts the abstract form of a "table status" message in '*ts' into an
1964 * OpenFlow message suitable for 'protocol', and returns that encoded form in
1965 * a buffer owned by the caller. */
1967 ofputil_encode_table_status(const struct ofputil_table_status
*ts
,
1968 enum ofputil_protocol protocol
)
1970 enum ofp_version version
;
1973 version
= ofputil_protocol_to_ofp_version(protocol
);
1974 if (version
>= OFP14_VERSION
) {
1976 struct ofp14_table_status
*ots
;
1978 raw
= OFPRAW_OFPT14_TABLE_STATUS
;
1979 b
= ofpraw_alloc_xid(raw
, version
, htonl(0), 0);
1980 ots
= ofpbuf_put_zeros(b
, sizeof *ots
);
1981 ots
->reason
= ts
->reason
;
1982 ofputil_put_ofp14_table_desc(&ts
->desc
, b
, version
);
1983 ofpmsg_update_length(b
);
1990 /* Decodes the OpenFlow "table status" message in '*ots' into an abstract form
1991 * in '*ts'. Returns 0 if successful, otherwise an OFPERR_* value. */
1993 ofputil_decode_table_status(const struct ofp_header
*oh
,
1994 struct ofputil_table_status
*ts
)
1996 const struct ofp14_table_status
*ots
;
2001 ofpbuf_use_const(&b
, oh
, ntohs(oh
->length
));
2002 raw
= ofpraw_pull_assert(&b
);
2003 ots
= ofpbuf_pull(&b
, sizeof *ots
);
2005 if (raw
== OFPRAW_OFPT14_TABLE_STATUS
) {
2006 if (ots
->reason
!= OFPTR_VACANCY_DOWN
2007 && ots
->reason
!= OFPTR_VACANCY_UP
) {
2008 return OFPERR_OFPBPC_BAD_VALUE
;
2010 ts
->reason
= ots
->reason
;
2012 error
= ofputil_decode_table_desc(&b
, &ts
->desc
, oh
->version
);
2015 return OFPERR_OFPBRC_BAD_VERSION
;
2022 ofputil_format_table_status(struct ds
*string
,
2023 const struct ofputil_table_status
*ts
,
2024 const struct ofputil_table_map
*table_map
)
2026 if (ts
->reason
== OFPTR_VACANCY_DOWN
) {
2027 ds_put_format(string
, " reason=VACANCY_DOWN");
2028 } else if (ts
->reason
== OFPTR_VACANCY_UP
) {
2029 ds_put_format(string
, " reason=VACANCY_UP");
2032 ds_put_format(string
, "\ntable_desc:-");
2033 ofputil_table_desc_format(string
, &ts
->desc
, table_map
);