summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/openvswitch/netdev.h2
-rw-r--r--include/openvswitch/ofp-errors.h6
-rw-r--r--include/openvswitch/ofp-flow.h17
-rw-r--r--include/openvswitch/ofp-match.h5
-rw-r--r--include/openvswitch/ofp-monitor.h17
-rw-r--r--include/openvswitch/ofp-packet.h11
-rw-r--r--include/openvswitch/ofp-port.h13
-rw-r--r--include/openvswitch/ofp-print.h22
-rw-r--r--include/openvswitch/ofp-switch.h5
-rw-r--r--include/openvswitch/ofp-table.h51
-rw-r--r--include/openvswitch/ofp-util.h1
-rw-r--r--lib/netdev.c35
-rw-r--r--lib/ofp-errors.c19
-rw-r--r--lib/ofp-flow.c257
-rw-r--r--lib/ofp-match.c154
-rw-r--r--lib/ofp-monitor.c162
-rw-r--r--lib/ofp-packet.c161
-rw-r--r--lib/ofp-port.c218
-rw-r--r--lib/ofp-print.c1561
-rw-r--r--lib/ofp-switch.c62
-rw-r--r--lib/ofp-table.c386
-rw-r--r--lib/ofp-util.c17
-rw-r--r--ovn/utilities/ovn-sbctl.c2
-rw-r--r--ovn/utilities/ovn-trace.c2
-rw-r--r--utilities/ovs-ofctl.c12
25 files changed, 1670 insertions, 1528 deletions
diff --git a/include/openvswitch/netdev.h b/include/openvswitch/netdev.h
index e25c241f4..0c10f7b48 100644
--- a/include/openvswitch/netdev.h
+++ b/include/openvswitch/netdev.h
@@ -26,6 +26,7 @@ extern "C" {
#endif
struct netdev;
+struct ds;
/* Maximum name length for custom statistics counters */
#define NETDEV_CUSTOM_STATS_NAME_SIZE 64
@@ -129,6 +130,7 @@ uint64_t netdev_features_to_bps(enum netdev_features features,
uint64_t default_bps);
bool netdev_features_is_full_duplex(enum netdev_features features);
int netdev_set_advertisements(struct netdev *, enum netdev_features advertise);
+void netdev_features_format(struct ds *, enum netdev_features);
void netdev_free_custom_stats_counters(struct netdev_custom_stats *);
diff --git a/include/openvswitch/ofp-errors.h b/include/openvswitch/ofp-errors.h
index 6542f10b4..16feacb55 100644
--- a/include/openvswitch/ofp-errors.h
+++ b/include/openvswitch/ofp-errors.h
@@ -29,6 +29,8 @@ extern "C" {
struct ds;
struct ofpbuf;
+struct ofputil_port_map;
+struct ofputil_table_map;
/* Error codes.
*
@@ -898,6 +900,10 @@ enum ofperr ofperr_decode_msg(const struct ofp_header *,
struct ofpbuf *ofperr_encode_reply(enum ofperr, const struct ofp_header *);
struct ofpbuf *ofperr_encode_hello(enum ofperr, enum ofp_version ofp_version,
const char *);
+void ofperr_msg_format(struct ds *, enum ofperr, const struct ofpbuf *payload,
+ const struct ofputil_port_map *,
+ const struct ofputil_table_map *);
+
int ofperr_get_vendor(enum ofperr, enum ofp_version);
int ofperr_get_type(enum ofperr, enum ofp_version);
int ofperr_get_code(enum ofperr, enum ofp_version);
diff --git a/include/openvswitch/ofp-flow.h b/include/openvswitch/ofp-flow.h
index 28aa77bad..17d48f12e 100644
--- a/include/openvswitch/ofp-flow.h
+++ b/include/openvswitch/ofp-flow.h
@@ -64,6 +64,8 @@ enum ofputil_flow_mod_flags {
to be modified */
};
+void ofputil_flow_mod_flags_format(struct ds *, enum ofputil_flow_mod_flags);
+
/* Protocol-independent flow_mod.
*
* The handling of cookies across multiple versions of OpenFlow is a bit
@@ -123,6 +125,10 @@ enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
uint8_t max_table);
struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *,
enum ofputil_protocol);
+enum ofperr ofputil_flow_mod_format(struct ds *, const struct ofp_header *,
+ const struct ofputil_port_map *,
+ const struct ofputil_table_map *,
+ int verbosity);
char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
const struct ofputil_port_map *,
@@ -172,6 +178,10 @@ char *parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *,
enum ofputil_protocol *usable_protocols)
OVS_WARN_UNUSED_RESULT;
+void ofputil_flow_stats_request_format(
+ struct ds *, const struct ofputil_flow_stats_request *,
+ const struct ofputil_port_map *, const struct ofputil_table_map *);
+
/* Flow stats reply, independent of protocol. */
struct ofputil_flow_stats {
struct match match;
@@ -200,6 +210,11 @@ void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *,
struct ovs_list *replies,
const struct tun_table *);
+void ofputil_flow_stats_format(struct ds *, const struct ofputil_flow_stats *,
+ const struct ofputil_port_map *,
+ const struct ofputil_table_map *,
+ bool show_stats);
+
/* Aggregate stats reply, independent of protocol. */
struct ofputil_aggregate_stats {
uint64_t packet_count; /* Packet count, UINT64_MAX if unknown. */
@@ -213,6 +228,8 @@ struct ofpbuf *ofputil_encode_aggregate_stats_reply(
enum ofperr ofputil_decode_aggregate_stats_reply(
struct ofputil_aggregate_stats *,
const struct ofp_header *reply);
+void ofputil_aggregate_stats_format(struct ds *,
+ const struct ofputil_aggregate_stats *);
#ifdef __cplusplus
}
diff --git a/include/openvswitch/ofp-match.h b/include/openvswitch/ofp-match.h
index c3baa8e8f..6ed373e2b 100644
--- a/include/openvswitch/ofp-match.h
+++ b/include/openvswitch/ofp-match.h
@@ -24,6 +24,7 @@
struct vl_mff_map;
struct flow_wildcards;
struct match;
+struct ofputil_port_map;
struct tun_table;
#ifdef __cplusplus
@@ -37,6 +38,10 @@ void ofputil_match_from_ofp10_match(const struct ofp10_match *,
void ofputil_normalize_match(struct match *);
void ofputil_normalize_match_quiet(struct match *);
void ofputil_match_to_ofp10_match(const struct match *, struct ofp10_match *);
+void ofp10_match_print(struct ds *, const struct ofp10_match *,
+ const struct ofputil_port_map *, int verbosity);
+char *ofp10_match_to_string(const struct ofp10_match *,
+ const struct ofputil_port_map *, int verbosity);
/* Work with ofp11_match. */
enum ofperr ofputil_pull_ofp11_match(struct ofpbuf *, const struct tun_table *,
diff --git a/include/openvswitch/ofp-monitor.h b/include/openvswitch/ofp-monitor.h
index 7680440d4..bd225518e 100644
--- a/include/openvswitch/ofp-monitor.h
+++ b/include/openvswitch/ofp-monitor.h
@@ -29,6 +29,9 @@ extern "C" {
struct ofputil_table_map;
+const char *ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason,
+ char *reasonbuf, size_t bufsize);
+
/* Flow removed message, independent of protocol. */
struct ofputil_flow_removed {
struct match match;
@@ -48,6 +51,10 @@ enum ofperr ofputil_decode_flow_removed(struct ofputil_flow_removed *,
const struct ofp_header *);
struct ofpbuf *ofputil_encode_flow_removed(const struct ofputil_flow_removed *,
enum ofputil_protocol);
+void ofputil_flow_removed_format(struct ds *,
+ const struct ofputil_flow_removed *,
+ const struct ofputil_port_map *,
+ const struct ofputil_table_map *);
/* Abstract nx_flow_monitor_request. */
struct ofputil_flow_monitor_request {
@@ -62,6 +69,10 @@ int ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *,
struct ofpbuf *msg);
void ofputil_append_flow_monitor_request(
const struct ofputil_flow_monitor_request *, struct ofpbuf *msg);
+void ofputil_flow_monitor_request_format(
+ struct ds *, const struct ofputil_flow_monitor_request *,
+ const struct ofputil_port_map *, const struct ofputil_table_map *);
+
char *parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
const char *,
const struct ofputil_port_map *,
@@ -94,6 +105,10 @@ void ofputil_start_flow_update(struct ovs_list *replies);
void ofputil_append_flow_update(const struct ofputil_flow_update *,
struct ovs_list *replies,
const struct tun_table *);
+void ofputil_flow_update_format(struct ds *,
+ const struct ofputil_flow_update *,
+ const struct ofputil_port_map *,
+ const struct ofputil_table_map *);
/* Abstract nx_flow_monitor_cancel. */
uint32_t ofputil_decode_flow_monitor_cancel(const struct ofp_header *);
@@ -120,8 +135,6 @@ enum ofperr ofputil_decode_requestforward(const struct ofp_header *,
struct ofputil_requestforward *);
void ofputil_destroy_requestforward(struct ofputil_requestforward *);
-
-
#ifdef __cplusplus
}
#endif
diff --git a/include/openvswitch/ofp-packet.h b/include/openvswitch/ofp-packet.h
index f96e36613..67001cb3f 100644
--- a/include/openvswitch/ofp-packet.h
+++ b/include/openvswitch/ofp-packet.h
@@ -154,6 +154,12 @@ enum ofperr ofputil_decode_packet_in_private(
struct ofputil_packet_in_private *,
size_t *total_len, uint32_t *buffer_id);
+void ofputil_packet_in_private_format(
+ struct ds *, const struct ofputil_packet_in_private *,
+ size_t total_len, uint32_t buffer_id,
+ const struct ofputil_port_map *,
+ const struct ofputil_table_map *, int verbosity);
+
void ofputil_packet_in_private_destroy(struct ofputil_packet_in_private *);
/* Abstract packet-out message.
@@ -176,6 +182,11 @@ enum ofperr ofputil_decode_packet_out(struct ofputil_packet_out *,
struct ofpbuf *ofputil_encode_packet_out(const struct ofputil_packet_out *,
enum ofputil_protocol protocol);
+void ofputil_packet_out_format(struct ds *, const struct ofputil_packet_out *,
+ const struct ofputil_port_map *,
+ const struct ofputil_table_map *,
+ int verbosity);
+
char *parse_ofp_packet_out_str(struct ofputil_packet_out *, const char *,
const struct ofputil_port_map *,
const struct ofputil_table_map *,
diff --git a/include/openvswitch/ofp-port.h b/include/openvswitch/ofp-port.h
index 59ca0373b..4286ba19d 100644
--- a/include/openvswitch/ofp-port.h
+++ b/include/openvswitch/ofp-port.h
@@ -19,6 +19,7 @@
#include "openvswitch/hmap.h"
#include "openvswitch/netdev.h"
+#include "openvswitch/ofp-errors.h"
#include "openvswitch/ofp-protocol.h"
#include "openvswitch/namemap.h"
@@ -71,6 +72,8 @@ enum ofputil_port_config {
/* There are no OpenFlow 1.1-only bits. */
};
+void ofputil_port_config_format(struct ds *, enum ofputil_port_config);
+
enum ofputil_port_state {
/* OpenFlow 1.0 and 1.1 share this values for these port state bits. */
OFPUTIL_PS_LINK_DOWN = 1 << 0, /* No physical link present. */
@@ -85,6 +88,8 @@ enum ofputil_port_state {
OFPUTIL_PS_STP_MASK = 3 << 8 /* Bit mask for OFPPS10_STP_* values. */
};
+void ofputil_port_state_format(struct ds *, enum ofputil_port_state);
+
/* Abstract ofp10_phy_port, ofp11_port, ofp14_port, or ofp16_port. */
struct ofputil_phy_port {
ofp_port_t port_no;
@@ -112,11 +117,13 @@ struct ofputil_phy_port {
uint32_t max_speed; /* Maximum supported speed, in kbps. */
};
-/* phy_port helper functions. */
void ofputil_put_phy_port(enum ofp_version,
const struct ofputil_phy_port *, struct ofpbuf *);
int ofputil_pull_phy_port(enum ofp_version, struct ofpbuf *,
struct ofputil_phy_port *);
+void ofputil_phy_port_format(struct ds *, const struct ofputil_phy_port *);
+enum ofperr ofputil_phy_ports_format(struct ds *, uint8_t ofp_version,
+ struct ofpbuf *);
/* Abstract ofp_port_status. */
struct ofputil_port_status {
@@ -128,6 +135,8 @@ enum ofperr ofputil_decode_port_status(const struct ofp_header *,
struct ofputil_port_status *);
struct ofpbuf *ofputil_encode_port_status(const struct ofputil_port_status *,
enum ofputil_protocol);
+void ofputil_port_status_format(struct ds *,
+ const struct ofputil_port_status *);
/* Abstract ofp_port_mod. */
struct ofputil_port_mod {
@@ -143,6 +152,8 @@ enum ofperr ofputil_decode_port_mod(const struct ofp_header *,
struct ofputil_port_mod *, bool loose);
struct ofpbuf *ofputil_encode_port_mod(const struct ofputil_port_mod *,
enum ofputil_protocol);
+void ofputil_port_mod_format(struct ds *, const struct ofputil_port_mod *,
+ const struct ofputil_port_map *);
struct ofputil_port_stats {
ofp_port_t port_no;
diff --git a/include/openvswitch/ofp-print.h b/include/openvswitch/ofp-print.h
index ed113786a..d76f06872 100644
--- a/include/openvswitch/ofp-print.h
+++ b/include/openvswitch/ofp-print.h
@@ -25,7 +25,6 @@
#include <openvswitch/types.h>
struct ds;
-struct ofp10_match;
struct ofp_flow_mod;
struct ofp_header;
struct ofputil_flow_stats;
@@ -45,28 +44,17 @@ void ofp_print_packet(FILE *stream, const void *data,
size_t len, ovs_be32 packet_type);
void ofp_print_dp_packet(FILE *stream, const struct dp_packet *packet);
-void ofp10_match_print(struct ds *, const struct ofp10_match *,
- const struct ofputil_port_map *, int verbosity);
-
char *ofp_to_string(const void *, size_t, const struct ofputil_port_map *,
const struct ofputil_table_map *, int verbosity);
-char *ofp10_match_to_string(const struct ofp10_match *,
- const struct ofputil_port_map *, int verbosity);
char *ofp_packet_to_string(const void *data, size_t len, ovs_be32 packet_type);
char *ofp_dp_packet_to_string(const struct dp_packet *packet);
void ofp_print_version(const struct ofp_header *, struct ds *);
-void ofp_print_table_features(
- struct ds *, const struct ofputil_table_features *features,
- const struct ofputil_table_features *prev_features,
- const struct ofputil_table_stats *stats,
- const struct ofputil_table_stats *prev_stats,
- const struct ofputil_table_map *table_map);
-
-void ofp_print_flow_stats(struct ds *, const struct ofputil_flow_stats *,
- const struct ofputil_port_map *,
- const struct ofputil_table_map *,
- bool show_stats);
+
+void ofp_print_duration(struct ds *, unsigned int sec, unsigned int nsec);
+void ofp_print_bit_names(struct ds *, uint32_t bits,
+ const char *(*bit_to_name)(uint32_t bit),
+ char separator);
#ifdef __cplusplus
}
diff --git a/include/openvswitch/ofp-switch.h b/include/openvswitch/ofp-switch.h
index 737cd61e7..6f75a2533 100644
--- a/include/openvswitch/ofp-switch.h
+++ b/include/openvswitch/ofp-switch.h
@@ -71,6 +71,8 @@ struct ofpbuf *ofputil_encode_switch_features(
ovs_be32 xid);
void ofputil_put_switch_features_port(const struct ofputil_phy_port *,
struct ofpbuf *);
+void ofputil_switch_features_format(struct ds *,
+ const struct ofputil_switch_features *);
bool ofputil_switch_features_has_ports(struct ofpbuf *b);
enum ofputil_frag_handling {
@@ -108,6 +110,9 @@ enum ofperr ofputil_decode_set_config(const struct ofp_header *,
struct ofpbuf *ofputil_encode_set_config(
const struct ofputil_switch_config *, enum ofp_version);
+void ofputil_switch_config_format(struct ds *,
+ const struct ofputil_switch_config *);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/openvswitch/ofp-table.h b/include/openvswitch/ofp-table.h
index 47b0fae80..e8260657f 100644
--- a/include/openvswitch/ofp-table.h
+++ b/include/openvswitch/ofp-table.h
@@ -26,6 +26,8 @@
extern "C" {
#endif
+struct ofputil_table_stats;
+
/* Abstract version of OFPTC11_TABLE_MISS_*.
*
* OpenFlow 1.0 always sends packets that miss to the next flow table, or to
@@ -52,6 +54,8 @@ enum ofputil_table_miss {
OFPUTIL_TABLE_MISS_DROP, /* Drop the packet. */
};
+const char *ofputil_table_miss_to_string(enum ofputil_table_miss);
+
/* Abstract version of OFPTC14_EVICTION.
*
* OpenFlow 1.0 through 1.3 don't know anything about eviction, so decoding a
@@ -63,6 +67,8 @@ enum ofputil_table_eviction {
OFPUTIL_TABLE_EVICTION_OFF /* Disable eviction. */
};
+const char *ofputil_table_eviction_to_string(enum ofputil_table_eviction);
+
/* Abstract version of OFPTC14_VACANCY_EVENTS.
*
* OpenFlow 1.0 through 1.3 don't know anything about vacancy events, so
@@ -74,6 +80,8 @@ enum ofputil_table_vacancy {
OFPUTIL_TABLE_VACANCY_OFF /* Disable vacancy events. */
};
+const char *ofputil_table_vacancy_to_string(enum ofputil_table_vacancy);
+
/* Abstract version of OFPTMPT_VACANCY.
*
* Openflow 1.4+ defines vacancy events.
@@ -141,25 +149,37 @@ struct ofputil_table_mod {
struct ofputil_table_mod_prop_vacancy table_vacancy;
};
-/* Abstract ofp14_table_desc. */
-struct ofputil_table_desc {
- uint8_t table_id; /* ID of the table. */
- enum ofputil_table_eviction eviction;
- uint32_t eviction_flags; /* UINT32_MAX if not present. */
- enum ofputil_table_vacancy vacancy;
- struct ofputil_table_mod_prop_vacancy table_vacancy;
-};
-
enum ofperr ofputil_decode_table_mod(const struct ofp_header *,
struct ofputil_table_mod *);
struct ofpbuf *ofputil_encode_table_mod(const struct ofputil_table_mod *,
enum ofputil_protocol);
+void ofputil_table_mod_format(struct ds *, const struct ofputil_table_mod *,
+ const struct ofputil_table_map *);
char *parse_ofp_table_mod(struct ofputil_table_mod *,
const char *table_id, const char *flow_miss_handling,
const struct ofputil_table_map *,
uint32_t *usable_versions)
OVS_WARN_UNUSED_RESULT;
+/* Abstract ofp14_table_desc. */
+struct ofputil_table_desc {
+ uint8_t table_id; /* ID of the table. */
+ enum ofputil_table_eviction eviction;
+ uint32_t eviction_flags; /* UINT32_MAX if not present. */
+ enum ofputil_table_vacancy vacancy;
+ struct ofputil_table_mod_prop_vacancy table_vacancy;
+};
+
+int ofputil_decode_table_desc(struct ofpbuf *,
+ struct ofputil_table_desc *,
+ enum ofp_version);
+void ofputil_append_table_desc_reply(const struct ofputil_table_desc *td,
+ struct ovs_list *replies,
+ enum ofp_version);
+void ofputil_table_desc_format(struct ds *,
+ const struct ofputil_table_desc *,
+ const struct ofputil_table_map *);
+
/* Abstract ofp_table_features.
*
* This is used for all versions of OpenFlow, even though ofp_table_features
@@ -244,10 +264,6 @@ struct ofputil_table_features {
int ofputil_decode_table_features(struct ofpbuf *,
struct ofputil_table_features *, bool loose);
-int ofputil_decode_table_desc(struct ofpbuf *,
- struct ofputil_table_desc *,
- enum ofp_version);
-
struct ofpbuf *ofputil_encode_table_features_request(enum ofp_version);
struct ofpbuf *ofputil_encode_table_desc_request(enum ofp_version);
@@ -255,9 +271,12 @@ struct ofpbuf *ofputil_encode_table_desc_request(enum ofp_version);
void ofputil_append_table_features_reply(
const struct ofputil_table_features *tf, struct ovs_list *replies);
-void ofputil_append_table_desc_reply(const struct ofputil_table_desc *td,
- struct ovs_list *replies,
- enum ofp_version);
+void ofputil_table_features_format(
+ struct ds *, const struct ofputil_table_features *features,
+ const struct ofputil_table_features *prev_features,
+ const struct ofputil_table_stats *stats,
+ const struct ofputil_table_stats *prev_stats,
+ const struct ofputil_table_map *table_map);
/* Abstract table stats.
*
diff --git a/include/openvswitch/ofp-util.h b/include/openvswitch/ofp-util.h
index 1f4b23eb5..091a09cad 100644
--- a/include/openvswitch/ofp-util.h
+++ b/include/openvswitch/ofp-util.h
@@ -30,6 +30,7 @@ extern "C" {
bool ofputil_decode_hello(const struct ofp_header *,
uint32_t *allowed_versions);
struct ofpbuf *ofputil_encode_hello(uint32_t version_bitmap);
+void ofputil_hello_format(struct ds *, const struct ofp_header *);
struct ofpbuf *ofputil_encode_echo_request(enum ofp_version);
struct ofpbuf *ofputil_encode_echo_reply(const struct ofp_header *);
diff --git a/lib/netdev.c b/lib/netdev.c
index 5a97ce53e..b303a7dc5 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -45,6 +45,7 @@
#include "odp-netlink.h"
#include "openflow/openflow.h"
#include "packets.h"
+#include "openvswitch/ofp-print.h"
#include "openvswitch/poll-loop.h"
#include "seq.h"
#include "openvswitch/shash.h"
@@ -1095,6 +1096,40 @@ netdev_set_advertisements(struct netdev *netdev,
: EOPNOTSUPP);
}
+static const char *
+netdev_feature_to_name(uint32_t bit)
+{
+ enum netdev_features f = bit;
+
+ switch (f) {
+ case NETDEV_F_10MB_HD: return "10MB-HD";
+ case NETDEV_F_10MB_FD: return "10MB-FD";
+ case NETDEV_F_100MB_HD: return "100MB-HD";
+ case NETDEV_F_100MB_FD: return "100MB-FD";
+ case NETDEV_F_1GB_HD: return "1GB-HD";
+ case NETDEV_F_1GB_FD: return "1GB-FD";
+ case NETDEV_F_10GB_FD: return "10GB-FD";
+ case NETDEV_F_40GB_FD: return "40GB-FD";
+ case NETDEV_F_100GB_FD: return "100GB-FD";
+ case NETDEV_F_1TB_FD: return "1TB-FD";
+ case NETDEV_F_OTHER: return "OTHER";
+ case NETDEV_F_COPPER: return "COPPER";
+ case NETDEV_F_FIBER: return "FIBER";
+ case NETDEV_F_AUTONEG: return "AUTO_NEG";
+ case NETDEV_F_PAUSE: return "AUTO_PAUSE";
+ case NETDEV_F_PAUSE_ASYM: return "AUTO_PAUSE_ASYM";
+ }
+
+ return NULL;
+}
+
+void
+netdev_features_format(struct ds *s, enum netdev_features features)
+{
+ ofp_print_bit_names(s, features, netdev_feature_to_name, ' ');
+ ds_put_char(s, '\n');
+}
+
/* Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask. If
* 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared. Returns a
* positive errno value. */
diff --git a/lib/ofp-errors.c b/lib/ofp-errors.c
index bdae00c74..3ca09cb68 100644
--- a/lib/ofp-errors.c
+++ b/lib/ofp-errors.c
@@ -22,6 +22,7 @@
#include "openvswitch/dynamic-string.h"
#include "openvswitch/ofp-errors.h"
#include "openvswitch/ofp-msgs.h"
+#include "openvswitch/ofp-print.h"
#include "openvswitch/ofpbuf.h"
#include "openvswitch/vlog.h"
#include "util.h"
@@ -240,6 +241,24 @@ ofperr_encode_hello(enum ofperr error, enum ofp_version ofp_version,
return ofperr_encode_msg__(error, ofp_version, htonl(0), s, strlen(s));
}
+void
+ofperr_msg_format(struct ds *string, enum ofperr error,
+ const struct ofpbuf *payload,
+ const struct ofputil_port_map *port_map,
+ const struct ofputil_table_map *table_map)
+{
+ ds_put_format(string, " %s\n", ofperr_get_name(error));
+
+ if (error == OFPERR_OFPHFC_INCOMPATIBLE || error == OFPERR_OFPHFC_EPERM) {
+ ds_put_printable(string, payload->data, payload->size);
+ } else {
+ char *s = ofp_to_string(payload->data, payload->size,
+ port_map, table_map, 1);
+ ds_put_cstr(string, s);
+ free(s);
+ }
+}
+
int
ofperr_get_vendor(enum ofperr error, enum ofp_version version)
{
diff --git a/lib/ofp-flow.c b/lib/ofp-flow.c
index 10d682599..cffadb303 100644
--- a/lib/ofp-flow.c
+++ b/lib/ofp-flow.c
@@ -18,6 +18,7 @@
#include "openvswitch/ofp-flow.h"
#include <errno.h>
#include "byte-order.h"
+#include "colors.h"
#include "flow.h"
#include "nx-match.h"
#include "openvswitch/ofp-actions.h"
@@ -26,6 +27,7 @@
#include "openvswitch/ofp-msgs.h"
#include "openvswitch/ofp-parse.h"
#include "openvswitch/ofp-port.h"
+#include "openvswitch/ofp-print.h"
#include "openvswitch/ofp-table.h"
#include "openvswitch/ofpbuf.h"
#include "openvswitch/vlog.h"
@@ -103,6 +105,32 @@ ofputil_encode_flow_mod_flags(enum ofputil_flow_mod_flags flags,
return htons(raw_flags);
}
+void
+ofputil_flow_mod_flags_format(struct ds *s, enum ofputil_flow_mod_flags flags)
+{
+ if (flags & OFPUTIL_FF_SEND_FLOW_REM) {
+ ds_put_cstr(s, "send_flow_rem ");
+ }
+ if (flags & OFPUTIL_FF_CHECK_OVERLAP) {
+ ds_put_cstr(s, "check_overlap ");
+ }
+ if (flags & OFPUTIL_FF_RESET_COUNTS) {
+ ds_put_cstr(s, "reset_counts ");
+ }
+ if (flags & OFPUTIL_FF_NO_PKT_COUNTS) {
+ ds_put_cstr(s, "no_packet_counts ");
+ }
+ if (flags & OFPUTIL_FF_NO_BYT_COUNTS) {
+ ds_put_cstr(s, "no_byte_counts ");
+ }
+ if (flags & OFPUTIL_FF_HIDDEN_FIELDS) {
+ ds_put_cstr(s, "allow_hidden_fields ");
+ }
+ if (flags & OFPUTIL_FF_NO_READONLY) {
+ ds_put_cstr(s, "no_readonly_table ");
+ }
+}
+
/* Converts an OFPT_FLOW_MOD or NXT_FLOW_MOD message 'oh' into an abstract
* flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error
* code.
@@ -438,6 +466,133 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
return msg;
}
+enum ofperr
+ofputil_flow_mod_format(struct ds *s, const struct ofp_header *oh,
+ const struct ofputil_port_map *port_map,
+ const struct ofputil_table_map *table_map,
+ int verbosity)
+{
+ struct ofputil_flow_mod fm;
+ struct ofpbuf ofpacts;
+ bool need_priority;
+ enum ofperr error;
+ enum ofpraw raw;
+ enum ofputil_protocol protocol;
+
+ protocol = ofputil_protocol_from_ofp_version(oh->version);
+ protocol = ofputil_protocol_set_tid(protocol, true);
+
+ ofpbuf_init(&ofpacts, 64);
+ error = ofputil_decode_flow_mod(&fm, oh, protocol, NULL, NULL, &ofpacts,
+ OFPP_MAX, 255);
+ if (error) {
+ ofpbuf_uninit(&ofpacts);
+ return error;
+ }
+
+ ds_put_char(s, ' ');
+ switch (fm.command) {
+ case OFPFC_ADD:
+ ds_put_cstr(s, "ADD");
+ break;
+ case OFPFC_MODIFY:
+ ds_put_cstr(s, "MOD");
+ break;
+ case OFPFC_MODIFY_STRICT:
+ ds_put_cstr(s, "MOD_STRICT");
+ break;
+ case OFPFC_DELETE:
+ ds_put_cstr(s, "DEL");
+ break;
+ case OFPFC_DELETE_STRICT:
+ ds_put_cstr(s, "DEL_STRICT");
+ break;
+ default:
+ ds_put_format(s, "cmd:%d", fm.command);
+ }
+ if (fm.table_id != 0
+ || ofputil_table_map_get_name(table_map, fm.table_id)) {
+ ds_put_format(s, " table:");
+ ofputil_format_table(fm.table_id, table_map, s);
+ }
+
+ ds_put_char(s, ' ');
+ ofpraw_decode(&raw, oh);
+ if (verbosity >= 3 && raw == OFPRAW_OFPT10_FLOW_MOD) {
+ const struct ofp10_flow_mod *ofm = ofpmsg_body(oh);
+ ofp10_match_print(s, &ofm->match, port_map, verbosity);
+
+ /* ofp_print_match() doesn't print priority. */
+ need_priority = true;
+ } else if (verbosity >= 3 && raw == OFPRAW_NXT_FLOW_MOD) {
+ const struct nx_flow_mod *nfm = ofpmsg_body(oh);
+ const void *nxm = nfm + 1;
+ char *nxm_s;
+
+ nxm_s = nx_match_to_string(nxm, ntohs(nfm->match_len));
+ ds_put_cstr(s, nxm_s);
+ free(nxm_s);
+
+ /* nx_match_to_string() doesn't print priority. */
+ need_priority = true;
+ } else {
+ match_format(&fm.match, port_map, s, fm.priority);
+
+ /* match_format() does print priority. */
+ need_priority = false;
+ }
+
+ if (ds_last(s) != ' ') {
+ ds_put_char(s, ' ');
+ }
+ if (fm.new_cookie != htonll(0) && fm.new_cookie != OVS_BE64_MAX) {
+ ds_put_format(s, "cookie:0x%"PRIx64" ", ntohll(fm.new_cookie));
+ }
+ if (fm.cookie_mask != htonll(0)) {
+ ds_put_format(s, "cookie:0x%"PRIx64"/0x%"PRIx64" ",
+ ntohll(fm.cookie), ntohll(fm.cookie_mask));
+ }
+ if (fm.idle_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(s, "idle:%"PRIu16" ", fm.idle_timeout);
+ }
+ if (fm.hard_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(s, "hard:%"PRIu16" ", fm.hard_timeout);
+ }
+ if (fm.importance != 0) {
+ ds_put_format(s, "importance:%"PRIu16" ", fm.importance);
+ }
+ if (fm.priority != OFP_DEFAULT_PRIORITY && need_priority) {
+ ds_put_format(s, "pri:%d ", fm.priority);
+ }
+ if (fm.buffer_id != UINT32_MAX) {
+ ds_put_format(s, "buf:0x%"PRIx32" ", fm.buffer_id);
+ }
+ if (fm.out_port != OFPP_ANY) {
+ ds_put_format(s, "out_port:");
+ ofputil_format_port(fm.out_port, port_map, s);
+ ds_put_char(s, ' ');
+ }
+
+ if (oh->version == OFP10_VERSION || oh->version == OFP11_VERSION) {
+ /* Don't print the reset_counts flag for OF1.0 and OF1.1 because those
+ * versions don't really have such a flag and printing one is likely to
+ * confuse people. */
+ fm.flags &= ~OFPUTIL_FF_RESET_COUNTS;
+ }
+ ofputil_flow_mod_flags_format(s, fm.flags);
+
+ ds_put_cstr(s, "actions=");
+ struct ofpact_format_params fp = {
+ .port_map = port_map,
+ .table_map = table_map,
+ .s = s,
+ };
+ ofpacts_format(fm.ofpacts, fm.ofpacts_len, &fp);
+ ofpbuf_uninit(&ofpacts);
+
+ return 0;
+}
+
static enum ofperr
ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr,
const struct ofp10_flow_stats_request *ofsr,
@@ -629,6 +784,26 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
return msg;
}
+void
+ofputil_flow_stats_request_format(struct ds *s,
+ const struct ofputil_flow_stats_request *fsr,
+ const struct ofputil_port_map *port_map,
+ const struct ofputil_table_map *table_map)
+{
+ if (fsr->table_id != 0xff) {
+ ds_put_format(s, " table=");
+ ofputil_format_table(fsr->table_id, table_map, s);
+ }
+
+ if (fsr->out_port != OFPP_ANY) {
+ ds_put_cstr(s, " out_port=");
+ ofputil_format_port(fsr->out_port, port_map, s);
+ }
+
+ ds_put_char(s, ' ');
+ match_format(&fsr->match, port_map, s, OFP_DEFAULT_PRIORITY);
+}
+
char * OVS_WARN_UNUSED_RESULT
parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
bool aggregate, const char *string,
@@ -969,6 +1144,80 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
fs_->match.flow.tunnel.metadata.tab = orig_tun_table;
}
+/* Appends a textual form of 'fs' to 'string', translating port numbers to
+ * names using 'port_map' (if provided). If 'show_stats' is true, the output
+ * includes the flow duration, packet and byte counts, and its idle and hard
+ * ages, otherwise they are omitted. */
+void
+ofputil_flow_stats_format(struct ds *string,
+ const struct ofputil_flow_stats *fs,
+ const struct ofputil_port_map *port_map,
+ const struct ofputil_table_map *table_map,
+ bool show_stats)
+{
+ if (show_stats || fs->cookie) {
+ ds_put_format(string, "%scookie=%s0x%"PRIx64", ",
+ colors.param, colors.end, ntohll(fs->cookie));
+ }
+ if (show_stats) {
+ ds_put_format(string, "%sduration=%s", colors.param, colors.end);
+ ofp_print_duration(string, fs->duration_sec, fs->duration_nsec);
+ ds_put_cstr(string, ", ");
+ }
+
+ if (show_stats || fs->table_id
+ || ofputil_table_map_get_name(table_map, fs->table_id) != NULL) {
+ ds_put_format(string, "%stable=%s", colors.special, colors.end);
+ ofputil_format_table(fs->table_id, table_map, string);
+ ds_put_cstr(string, ", ");
+ }
+ if (show_stats) {
+ ds_put_format(string, "%sn_packets=%s%"PRIu64", ",
+ colors.param, colors.end, fs->packet_count);
+ ds_put_format(string, "%sn_bytes=%s%"PRIu64", ",
+ colors.param, colors.end, fs->byte_count);
+ }
+ if (fs->idle_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(string, "%sidle_timeout=%s%"PRIu16", ",
+ colors.param, colors.end, fs->idle_timeout);
+ }
+ if (fs->hard_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(string, "%shard_timeout=%s%"PRIu16", ",
+ colors.param, colors.end, fs->hard_timeout);
+ }
+ if (fs->flags) {
+ ofputil_flow_mod_flags_format(string, fs->flags);
+ }
+ if (fs->importance != 0) {
+ ds_put_format(string, "%simportance=%s%"PRIu16", ",
+ colors.param, colors.end, fs->importance);
+ }
+ if (show_stats && fs->idle_age >= 0) {
+ ds_put_format(string, "%sidle_age=%s%d, ",
+ colors.param, colors.end, fs->idle_age);
+ }
+ if (show_stats && fs->hard_age >= 0 && fs->hard_age != fs->duration_sec) {
+ ds_put_format(string, "%shard_age=%s%d, ",
+ colors.param, colors.end, fs->hard_age);
+ }
+
+ /* Print the match, followed by a space (but omit the space if the match
+ * was an empty string). */
+ size_t length = string->length;
+ match_format(&fs->match, port_map, string, fs->priority);
+ if (string->length != length) {
+ ds_put_char(string, ' ');
+ }
+
+ ds_put_format(string, "%sactions=%s", colors.actions, colors.end);
+ struct ofpact_format_params fp = {
+ .port_map = port_map,
+ .table_map = table_map,
+ .s = string,
+ };
+ ofpacts_format(fs->ofpacts, fs->ofpacts_len, &fp);
+}
+
/* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or
* NXST_AGGREGATE reply matching 'request', and returns the message. */
struct ofpbuf *
@@ -1015,6 +1264,14 @@ ofputil_decode_aggregate_stats_reply(struct ofputil_aggregate_stats *stats,
return 0;
}
+void
+ofputil_aggregate_stats_format(struct ds *s,
+ const struct ofputil_aggregate_stats *as)
+{
+ ds_put_format(s, " packet_count=%"PRIu64, as->packet_count);
+ ds_put_format(s, " byte_count=%"PRIu64, as->byte_count);
+ ds_put_format(s, " flow_count=%"PRIu32, as->flow_count);
+}
/* Parses 'str_value' as the value of subfield 'name', and updates
* 'match' appropriately. Restricts the set of usable protocols to ones
diff --git a/lib/ofp-match.c b/lib/ofp-match.c
index c907f2908..acdf0b776 100644
--- a/lib/ofp-match.c
+++ b/lib/ofp-match.c
@@ -953,3 +953,157 @@ ofputil_normalize_match_quiet(struct match *match)
{
ofputil_normalize_match__(match, false);
}
+
+static void OVS_PRINTF_FORMAT(5, 6)
+print_wild(struct ds *string, const char *leader, int is_wild,
+ int verbosity, const char *format, ...)
+{
+ if (is_wild && verbosity < 2) {
+ return;
+ }
+ ds_put_cstr(string, leader);
+ if (!is_wild) {
+ va_list args;
+
+ va_start(args, format);
+ ds_put_format_valist(string, format, args);
+ va_end(args);
+ } else {
+ ds_put_char(string, '*');
+ }
+ ds_put_char(string, ',');
+}
+
+static void
+print_wild_port(struct ds *string, const char *leader, int is_wild,
+ int verbosity, ofp_port_t port,
+ const struct ofputil_port_map *port_map)
+{
+ if (is_wild && verbosity < 2) {
+ return;
+ }
+ ds_put_cstr(string, leader);
+ if (!is_wild) {
+ ofputil_format_port(port, port_map, string);
+ } else {
+ ds_put_char(string, '*');
+ }
+ ds_put_char(string, ',');
+}
+
+static void
+print_ip_netmask(struct ds *string, const char *leader, ovs_be32 ip,
+ uint32_t wild_bits, int verbosity)
+{
+ if (wild_bits >= 32 && verbosity < 2) {
+ return;
+ }
+ ds_put_cstr(string, leader);
+ if (wild_bits < 32) {
+ ds_put_format(string, IP_FMT, IP_ARGS(ip));
+ if (wild_bits) {
+ ds_put_format(string, "/%d", 32 - wild_bits);
+ }
+ } else {
+ ds_put_char(string, '*');
+ }
+ ds_put_char(string, ',');
+}
+
+void
+ofp10_match_print(struct ds *f, const struct ofp10_match *om,
+ const struct ofputil_port_map *port_map, int verbosity)
+{
+ char *s = ofp10_match_to_string(om, port_map, verbosity);
+ ds_put_cstr(f, s);
+ free(s);
+}
+
+char *
+ofp10_match_to_string(const struct ofp10_match *om,
+ const struct ofputil_port_map *port_map, int verbosity)
+{
+ struct ds f = DS_EMPTY_INITIALIZER;
+ uint32_t w = ntohl(om->wildcards);
+ bool skip_type = false;
+ bool skip_proto = false;
+
+ if (!(w & OFPFW10_DL_TYPE)) {
+ skip_type = true;
+ if (om->dl_type == htons(ETH_TYPE_IP)) {
+ if (!(w & OFPFW10_NW_PROTO)) {
+ skip_proto = true;
+ if (om->nw_proto == IPPROTO_ICMP) {
+ ds_put_cstr(&f, "icmp,");
+ } else if (om->nw_proto == IPPROTO_TCP) {
+ ds_put_cstr(&f, "tcp,");
+ } else if (om->nw_proto == IPPROTO_UDP) {
+ ds_put_cstr(&f, "udp,");
+ } else if (om->nw_proto == IPPROTO_SCTP) {
+ ds_put_cstr(&f, "sctp,");
+ } else {
+ ds_put_cstr(&f, "ip,");
+ skip_proto = false;
+ }
+ } else {
+ ds_put_cstr(&f, "ip,");
+ }
+ } else if (om->dl_type == htons(ETH_TYPE_ARP)) {
+ ds_put_cstr(&f, "arp,");
+ } else if (om->dl_type == htons(ETH_TYPE_RARP)){
+ ds_put_cstr(&f, "rarp,");
+ } else if (om->dl_type == htons(ETH_TYPE_MPLS)) {
+ ds_put_cstr(&f, "mpls,");
+ } else if (om->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ ds_put_cstr(&f, "mplsm,");
+ } else {
+ skip_type = false;
+ }
+ }
+ print_wild_port(&f, "in_port=", w & OFPFW10_IN_PORT, verbosity,
+ u16_to_ofp(ntohs(om->in_port)), port_map);
+ print_wild(&f, "dl_vlan=", w & OFPFW10_DL_VLAN, verbosity,
+ "%d", ntohs(om->dl_vlan));
+ print_wild(&f, "dl_vlan_pcp=", w & OFPFW10_DL_VLAN_PCP, verbosity,
+ "%d", om->dl_vlan_pcp);
+ print_wild(&f, "dl_src=", w & OFPFW10_DL_SRC, verbosity,
+ ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_src));
+ print_wild(&f, "dl_dst=", w & OFPFW10_DL_DST, verbosity,
+ ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_dst));
+ if (!skip_type) {
+ print_wild(&f, "dl_type=", w & OFPFW10_DL_TYPE, verbosity,
+ "0x%04x", ntohs(om->dl_type));
+ }
+ print_ip_netmask(&f, "nw_src=", om->nw_src,
+ (w & OFPFW10_NW_SRC_MASK) >> OFPFW10_NW_SRC_SHIFT,
+ verbosity);
+ print_ip_netmask(&f, "nw_dst=", om->nw_dst,
+ (w & OFPFW10_NW_DST_MASK) >> OFPFW10_NW_DST_SHIFT,
+ verbosity);
+ if (!skip_proto) {
+ if (om->dl_type == htons(ETH_TYPE_ARP) ||
+ om->dl_type == htons(ETH_TYPE_RARP)) {
+ print_wild(&f, "arp_op=", w & OFPFW10_NW_PROTO, verbosity,
+ "%u", om->nw_proto);
+ } else {
+ print_wild(&f, "nw_proto=", w & OFPFW10_NW_PROTO, verbosity,
+ "%u", om->nw_proto);
+ }
+ }
+ print_wild(&f, "nw_tos=", w & OFPFW10_NW_TOS, verbosity,
+ "%u", om->nw_tos);
+ if (om->nw_proto == IPPROTO_ICMP) {
+ print_wild(&f, "icmp_type=", w & OFPFW10_ICMP_TYPE, verbosity,
+ "%d", ntohs(om->tp_src));
+ print_wild(&f, "icmp_code=", w & OFPFW10_ICMP_CODE, verbosity,
+ "%d", ntohs(om->tp_dst));
+ } else {
+ print_wild(&f, "tp_src=", w & OFPFW10_TP_SRC, verbosity,
+ "%d", ntohs(om->tp_src));
+ print_wild(&f, "tp_dst=", w & OFPFW10_TP_DST, verbosity,
+ "%d", ntohs(om->tp_dst));
+ }
+ ds_chomp(&f, ',');
+ return ds_cstr(&f);
+}
+
diff --git a/lib/ofp-monitor.c b/lib/ofp-monitor.c
index 49d623c35..dd2a6bbeb 100644
--- a/lib/ofp-monitor.c
+++ b/lib/ofp-monitor.c
@@ -26,6 +26,7 @@
#include "openvswitch/ofp-meter.h"
#include "openvswitch/ofp-msgs.h"
#include "openvswitch/ofp-parse.h"
+#include "openvswitch/ofp-print.h"
#include "openvswitch/ofp-table.h"
#include "openvswitch/vlog.h"
@@ -33,6 +34,34 @@ VLOG_DEFINE_THIS_MODULE(ofp_monitor);
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+/* Returns a string form of 'reason'. The return value is either a statically
+ * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'.
+ * 'bufsize' should be at least OFP_FLOW_REMOVED_REASON_BUFSIZE. */
+#define OFP_FLOW_REMOVED_REASON_BUFSIZE (INT_STRLEN(int) + 1)
+const char *
+ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason,
+ char *reasonbuf, size_t bufsize)
+{
+ switch (reason) {
+ case OFPRR_IDLE_TIMEOUT:
+ return "idle";
+ case OFPRR_HARD_TIMEOUT:
+ return "hard";
+ case OFPRR_DELETE:
+ return "delete";
+ case OFPRR_GROUP_DELETE:
+ return "group_delete";
+ case OFPRR_EVICTION:
+ return "eviction";
+ case OFPRR_METER_DELETE:
+ return "meter_delete";
+ case OVS_OFPRR_NONE:
+ default:
+ snprintf(reasonbuf, bufsize, "%d", (int) reason);
+ return reasonbuf;
+ }
+}
+
/* Converts an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message 'oh' into an
* abstract ofputil_flow_removed in 'fr'. Returns 0 if successful, otherwise
* an OpenFlow error code. */
@@ -211,6 +240,41 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
return msg;
}
+
+void
+ofputil_flow_removed_format(struct ds *s,
+ const struct ofputil_flow_removed *fr,
+ const struct ofputil_port_map *port_map,
+ const struct ofputil_table_map *table_map)
+{
+ char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE];
+
+ ds_put_char(s, ' ');
+ match_format(&fr->match, port_map, s, fr->priority);
+
+ ds_put_format(s, " reason=%s",
+ ofp_flow_removed_reason_to_string(fr->reason, reasonbuf,
+ sizeof reasonbuf));
+
+ if (fr->table_id != 255) {
+ ds_put_format(s, " table_id=");
+ ofputil_format_table(fr->table_id, table_map, s);
+ }
+
+ if (fr->cookie != htonll(0)) {
+ ds_put_format(s, " cookie:0x%"PRIx64, ntohll(fr->cookie));
+ }
+ ds_put_cstr(s, " duration");
+ ofp_print_duration(s, fr->duration_sec, fr->duration_nsec);
+ ds_put_format(s, " idle%"PRIu16, fr->idle_timeout);
+ if (fr->hard_timeout) {
+ /* The hard timeout was only added in OF1.2, so only print it if it is
+ * actually in use to avoid gratuitous change to the formatting. */
+ ds_put_format(s, " hard%"PRIu16, fr->hard_timeout);
+ }
+ ds_put_format(s, " pkts%"PRIu64" bytes%"PRIu64"\n",
+ fr->packet_count, fr->byte_count);
+}
/* ofputil_flow_monitor_request */
@@ -291,6 +355,47 @@ ofputil_append_flow_monitor_request(
nfmr->table_id = rq->table_id;
}
+static const char *
+nx_flow_monitor_flags_to_name(uint32_t bit)
+{
+ enum nx_flow_monitor_flags fmf = bit;
+
+ switch (fmf) {
+ case NXFMF_INITIAL: return "initial";
+ case NXFMF_ADD: return "add";
+ case NXFMF_DELETE: return "delete";
+ case NXFMF_MODIFY: return "modify";
+ case NXFMF_ACTIONS: return "actions";
+ case NXFMF_OWN: return "own";
+ }
+
+ return NULL;
+}
+
+void
+ofputil_flow_monitor_request_format(
+ struct ds *s, const struct ofputil_flow_monitor_request *request,
+ const struct ofputil_port_map *port_map,
+ const struct ofputil_table_map *table_map)
+{
+ ds_put_format(s, "\n id=%"PRIu32" flags=", request->id);
+ ofp_print_bit_names(s, request->flags, nx_flow_monitor_flags_to_name, ',');
+
+ if (request->out_port != OFPP_NONE) {
+ ds_put_cstr(s, " out_port=");
+ ofputil_format_port(request->out_port, port_map, s);
+ }
+
+ if (request->table_id != 0xff) {
+ ds_put_format(s, " table=");
+ ofputil_format_table(request->table_id, table_map, s);
+ }
+
+ ds_put_char(s, ' ');
+ match_format(&request->match, port_map, s, OFP_DEFAULT_PRIORITY);
+ ds_chomp(s, ' ');
+}
+
static char * OVS_WARN_UNUSED_RESULT
parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr,
const char *str_,
@@ -567,6 +672,63 @@ ofputil_append_flow_update(const struct ofputil_flow_update *update,
ofpmp_postappend(replies, start_ofs);
update_->match.flow.tunnel.metadata.tab = orig_tun_table;
}
+
+void
+ofputil_flow_update_format(struct ds *s,
+ const struct ofputil_flow_update *update,
+ const struct ofputil_port_map *port_map,
+ const struct ofputil_table_map *table_map)
+{
+ char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE];
+
+ ds_put_cstr(s, "\n event=");
+ switch (update->event) {
+ case NXFME_ADDED:
+ ds_put_cstr(s, "ADDED");
+ break;
+
+ case NXFME_DELETED:
+ ds_put_format(s, "DELETED reason=%s",
+ ofp_flow_removed_reason_to_string(update->reason,
+ reasonbuf,
+ sizeof reasonbuf));
+ break;
+
+ case NXFME_MODIFIED:
+ ds_put_cstr(s, "MODIFIED");
+ break;
+
+ case NXFME_ABBREV:
+ ds_put_format(s, "ABBREV xid=0x%"PRIx32, ntohl(update->xid));
+ return;
+ }
+
+ ds_put_format(s, " table=");
+ ofputil_format_table(update->table_id, table_map, s);
+ if (update->idle_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(s, " idle_timeout=%"PRIu16, update->idle_timeout);
+ }
+ if (update->hard_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(s, " hard_timeout=%"PRIu16, update->hard_timeout);
+ }
+ ds_put_format(s, " cookie=%#"PRIx64, ntohll(update->cookie));
+
+ ds_put_char(s, ' ');
+ match_format(&update->match, port_map, s, OFP_DEFAULT_PRIORITY);
+
+ if (update->ofpacts_len) {
+ if (s->string[s->length - 1] != ' ') {
+ ds_put_char(s, ' ');
+ }
+ ds_put_cstr(s, "actions=");
+ struct ofpact_format_params fp = {
+ .port_map = port_map,
+ .table_map = table_map,
+ .s = s,
+ };
+ ofpacts_format(update->ofpacts, update->ofpacts_len, &fp);
+ }
+}
/* Encodes 'rf' according to 'protocol', and returns the encoded message.
* 'protocol' must be for OpenFlow 1.4 or later. */
diff --git a/lib/ofp-packet.c b/lib/ofp-packet.c
index 100f7c569..b74c29b61 100644
--- a/lib/ofp-packet.c
+++ b/lib/ofp-packet.c
@@ -23,8 +23,10 @@
#include "openvswitch/ofp-errors.h"
#include "openvswitch/ofp-msgs.h"
#include "openvswitch/ofp-parse.h"
+#include "openvswitch/ofp-print.h"
#include "openvswitch/ofp-port.h"
#include "openvswitch/ofp-prop.h"
+#include "openvswitch/ofp-table.h"
#include "openvswitch/ofpbuf.h"
#include "openvswitch/vlog.h"
#include "util.h"
@@ -895,6 +897,130 @@ ofputil_decode_packet_in_private(const struct ofp_header *oh, bool loose,
return error;
}
+static void
+format_hex_arg(struct ds *s, const uint8_t *data, size_t len)
+{
+ for (size_t i = 0; i < len; i++) {
+ if (i) {
+ ds_put_char(s, '.');
+ }
+ ds_put_format(s, "%02"PRIx8, data[i]);
+ }
+}
+
+void
+ofputil_packet_in_private_format(struct ds *s,
+ const struct ofputil_packet_in_private *pin,
+ size_t total_len, uint32_t buffer_id,
+ const struct ofputil_port_map *port_map,
+ const struct ofputil_table_map *table_map,
+ int verbosity)
+{
+ char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];
+ const struct ofputil_packet_in *public = &pin->base;
+
+ if (public->table_id
+ || ofputil_table_map_get_name(table_map, public->table_id)) {
+ ds_put_format(s, " table_id=");
+ ofputil_format_table(public->table_id, table_map, s);
+ }
+
+ if (public->cookie != OVS_BE64_MAX) {
+ ds_put_format(s, " cookie=0x%"PRIx64, ntohll(public->cookie));
+ }
+
+ ds_put_format(s, " total_len=%"PRIuSIZE" ", total_len);
+
+ match_format(&public->flow_metadata, port_map, s, OFP_DEFAULT_PRIORITY);
+
+ ds_put_format(s, " (via %s)",
+ ofputil_packet_in_reason_to_string(public->reason,
+ reasonbuf,
+ sizeof reasonbuf));
+
+ ds_put_format(s, " data_len=%"PRIuSIZE, public->packet_len);
+ if (buffer_id == UINT32_MAX) {
+ ds_put_format(s, " (unbuffered)");
+ if (total_len != public->packet_len) {
+ ds_put_format(s, " (***total_len != data_len***)");
+ }
+ } else {
+ ds_put_format(s, " buffer=0x%08"PRIx32, buffer_id);
+ if (total_len < public->packet_len) {
+ ds_put_format(s, " (***total_len < data_len***)");
+ }
+ }
+ ds_put_char(s, '\n');
+
+ if (public->userdata_len) {
+ ds_put_cstr(s, " userdata=");
+ format_hex_arg(s, pin->base.userdata, pin->base.userdata_len);
+ ds_put_char(s, '\n');
+ }
+
+ if (!uuid_is_zero(&pin->bridge)) {
+ ds_put_format(s, " continuation.bridge="UUID_FMT"\n",
+ UUID_ARGS(&pin->bridge));
+ }
+
+ if (pin->stack_size) {
+ ds_put_cstr(s, " continuation.stack=(top)");
+
+ struct ofpbuf pin_stack;
+ ofpbuf_use_const(&pin_stack, pin->stack, pin->stack_size);
+
+ while (pin_stack.size) {
+ uint8_t len;
+ uint8_t *val = nx_stack_pop(&pin_stack, &len);
+ union mf_subvalue value;
+
+ ds_put_char(s, ' ');
+ memset(&value, 0, sizeof value - len);
+ memcpy(&value.u8[sizeof value - len], val, len);
+ mf_subvalue_format(&value, s);
+ }
+ ds_put_cstr(s, " (bottom)\n");
+ }
+
+ if (pin->mirrors) {
+ ds_put_format(s, " continuation.mirrors=0x%"PRIx32"\n",
+ pin->mirrors);
+ }
+
+ if (pin->conntracked) {
+ ds_put_cstr(s, " continuation.conntracked=true\n");
+ }
+
+ struct ofpact_format_params fp = {
+ .port_map = port_map,
+ .table_map = table_map,
+ .s = s,
+ };
+
+ if (pin->actions_len) {
+ ds_put_cstr(s, " continuation.actions=");
+ ofpacts_format(pin->actions, pin->actions_len, &fp);
+ ds_put_char(s, '\n');
+ }
+
+ if (pin->action_set_len) {
+ ds_put_cstr(s, " continuation.action_set=");
+ ofpacts_format(pin->action_set, pin->action_set_len, &fp);
+ ds_put_char(s, '\n');
+ }
+
+ if (verbosity > 0) {
+ char *packet = ofp_packet_to_string(
+ public->packet, public->packet_len,
+ public->flow_metadata.flow.packet_type);
+ ds_put_cstr(s, packet);
+ free(packet);
+ }
+ if (verbosity > 2) {
+ ds_put_hex_dump(s, public->packet, public->packet_len, 0, false);
+ }
+}
+
/* Frees data in 'pin' that is dynamically allocated by
* ofputil_decode_packet_in_private().
*
@@ -1095,6 +1221,41 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po,
return msg;
}
+
+void
+ofputil_packet_out_format(struct ds *s, const struct ofputil_packet_out *po,
+ const struct ofputil_port_map *port_map,
+ const struct ofputil_table_map *table_map,
+ int verbosity)
+{
+ ds_put_char(s, ' ');
+ match_format(&po->flow_metadata, port_map, s, OFP_DEFAULT_PRIORITY);
+
+ ds_put_cstr(s, " actions=");
+ struct ofpact_format_params fp = {
+ .port_map = port_map,
+ .table_map = table_map,
+ .s = s,
+ };
+ ofpacts_format(po->ofpacts, po->ofpacts_len, &fp);
+
+ if (po->buffer_id == UINT32_MAX) {
+ ds_put_format(s, " data_len=%"PRIuSIZE, po->packet_len);
+ if (verbosity > 0 && po->packet_len > 0) {
+ ovs_be32 po_packet_type = po->flow_metadata.flow.packet_type;
+ char *packet = ofp_packet_to_string(po->packet, po->packet_len,
+ po_packet_type);
+ ds_put_char(s, '\n');
+ ds_put_cstr(s, packet);
+ free(packet);
+ }
+ if (verbosity > 2) {
+ ds_put_hex_dump(s, po->packet, po->packet_len, 0, false);
+ }
+ } else {
+ ds_put_format(s, " buffer=0x%08"PRIx32, po->buffer_id);
+ }
+}
/* Parse a string representation of a OFPT_PACKET_OUT to '*po'. If successful,
* both 'po->ofpacts' and 'po->packet' must be free()d by the caller. */
diff --git a/lib/ofp-port.c b/lib/ofp-port.c
index cff37290e..4d39299ed 100644
--- a/lib/ofp-port.c
+++ b/lib/ofp-port.c
@@ -16,12 +16,14 @@
#include <config.h>
#include "openvswitch/ofp-port.h"
+#include <ctype.h>
#include "byte-order.h"
#include "flow.h"
#include "openflow/intel-ext.h"
#include "openvswitch/json.h"
#include "openvswitch/ofp-errors.h"
#include "openvswitch/ofp-msgs.h"
+#include "openvswitch/ofp-print.h"
#include "openvswitch/ofp-prop.h"
#include "openvswitch/ofpbuf.h"
#include "openvswitch/vlog.h"
@@ -284,6 +286,83 @@ ofputil_port_to_string(ofp_port_t port,
snprintf(namebuf, bufsize, "%"PRIu32, port);
}
+/* ofputil_port_config */
+
+static const char *
+ofputil_port_config_to_name(uint32_t bit)
+{
+ enum ofputil_port_config pc = bit;
+
+ switch (pc) {
+ case OFPUTIL_PC_PORT_DOWN: return "PORT_DOWN";
+ case OFPUTIL_PC_NO_STP: return "NO_STP";
+ case OFPUTIL_PC_NO_RECV: return "NO_RECV";
+ case OFPUTIL_PC_NO_RECV_STP: return "NO_RECV_STP";
+ case OFPUTIL_PC_NO_FLOOD: return "NO_FLOOD";
+ case OFPUTIL_PC_NO_FWD: return "NO_FWD";
+ case OFPUTIL_PC_NO_PACKET_IN: return "NO_PACKET_IN";
+ }
+
+ return NULL;
+}
+
+void
+ofputil_port_config_format(struct ds *s, enum ofputil_port_config config)
+{
+ ofp_print_bit_names(s, config, ofputil_port_config_to_name, ' ');
+ ds_put_char(s, '\n');
+}
+
+/* ofputil_port_state */
+
+static const char *
+ofputil_port_state_to_name(uint32_t bit)
+{
+ enum ofputil_port_state ps = bit;
+
+ switch (ps) {
+ case OFPUTIL_PS_LINK_DOWN: return "LINK_DOWN";
+ case OFPUTIL_PS_BLOCKED: return "BLOCKED";
+ case OFPUTIL_PS_LIVE: return "LIVE";
+
+ case OFPUTIL_PS_STP_LISTEN:
+ case OFPUTIL_PS_STP_LEARN:
+ case OFPUTIL_PS_STP_FORWARD:
+ case OFPUTIL_PS_STP_BLOCK:
+ /* Handled elsewhere. */
+ return NULL;
+ }
+
+ return NULL;
+}
+
+void
+ofputil_port_state_format(struct ds *s, enum ofputil_port_state state)
+{
+ enum ofputil_port_state stp_state;
+
+ /* The STP state is a 2-bit field so it doesn't fit in with the bitmask
+ * pattern. We have to special case it.
+ *
+ * OVS doesn't support STP, so this field will always be 0 if we are
+ * talking to OVS, so we'd always print STP_LISTEN in that case.
+ * Therefore, we don't print anything at all if the value is STP_LISTEN, to
+ * avoid confusing users. */
+ stp_state = state & OFPUTIL_PS_STP_MASK;
+ if (stp_state) {
+ ds_put_cstr(s, (stp_state == OFPUTIL_PS_STP_LEARN ? "STP_LEARN"
+ : stp_state == OFPUTIL_PS_STP_FORWARD ? "STP_FORWARD"
+ : "STP_BLOCK"));
+ state &= ~OFPUTIL_PS_STP_MASK;
+ if (state) {
+ ofp_print_bit_names(s, state, ofputil_port_state_to_name, ' ');
+ }
+ } else {
+ ofp_print_bit_names(s, state, ofputil_port_state_to_name, ' ');
+ }
+ ds_put_char(s, '\n');
+}
+
/* ofputil_phy_port */
/* NETDEV_F_* to and from OFPPF_* and OFPPF10_*. */
@@ -722,6 +801,103 @@ ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b,
OVS_NOT_REACHED();
}
}
+
+void
+ofputil_phy_port_format(struct ds *s, const struct ofputil_phy_port *port)
+{
+ char name[sizeof port->name];
+ int j;
+
+ memcpy(name, port->name, sizeof name);
+ for (j = 0; j < sizeof name - 1; j++) {
+ if (!isprint((unsigned char) name[j])) {
+ break;
+ }
+ }
+ name[j] = '\0';
+
+ ds_put_char(s, ' ');
+ ofputil_format_port(port->port_no, NULL, s);
+ ds_put_format(s, "(%s): addr:"ETH_ADDR_FMT"\n",
+ name, ETH_ADDR_ARGS(port->hw_addr));
+
+ if (!eth_addr64_is_zero(port->hw_addr64)) {
+ ds_put_format(s, " addr64: "ETH_ADDR64_FMT"\n",
+ ETH_ADDR64_ARGS(port->hw_addr64));
+ }
+
+ ds_put_cstr(s, " config: ");
+ ofputil_port_config_format(s, port->config);
+
+ ds_put_cstr(s, " state: ");
+ ofputil_port_state_format(s, port->state);
+
+ if (port->curr) {
+ ds_put_format(s, " current: ");
+ netdev_features_format(s, port->curr);
+ }
+ if (port->advertised) {
+ ds_put_format(s, " advertised: ");
+ netdev_features_format(s, port->advertised);
+ }
+ if (port->supported) {
+ ds_put_format(s, " supported: ");
+ netdev_features_format(s, port->supported);
+ }
+ if (port->peer) {
+ ds_put_format(s, " peer: ");
+ netdev_features_format(s, port->peer);
+ }
+ ds_put_format(s, " speed: %"PRIu32" Mbps now, "
+ "%"PRIu32" Mbps max\n",
+ port->curr_speed / UINT32_C(1000),
+ port->max_speed / UINT32_C(1000));
+}
+
+/* qsort comparison function. */
+static int
+compare_ports(const void *a_, const void *b_)
+{
+ const struct ofputil_phy_port *a = a_;
+ const struct ofputil_phy_port *b = b_;
+ uint16_t ap = ofp_to_u16(a->port_no);
+ uint16_t bp = ofp_to_u16(b->port_no);
+
+ return ap < bp ? -1 : ap > bp;
+}
+
+/* Given a buffer 'b' that contains an array of OpenFlow ports of type
+ * 'ofp_version', writes a detailed description of each port into 'string'. */
+enum ofperr
+ofputil_phy_ports_format(struct ds *string, uint8_t ofp_version,
+ struct ofpbuf *b)
+{
+ struct ofputil_phy_port *ports;
+ size_t allocated_ports, n_ports;
+ int retval;
+ size_t i;
+
+ ports = NULL;
+ allocated_ports = 0;
+ for (n_ports = 0; ; n_ports++) {
+ if (n_ports >= allocated_ports) {
+ ports = x2nrealloc(ports, &allocated_ports, sizeof *ports);
+ }
+
+ retval = ofputil_pull_phy_port(ofp_version, b, &ports[n_ports]);
+ if (retval) {
+ break;
+ }
+ }
+
+ qsort(ports, n_ports, sizeof *ports, compare_ports);
+ for (i = 0; i < n_ports; i++) {
+ ofputil_phy_port_format(string, &ports[i]);
+ }
+ free(ports);
+
+ return retval != EOF ? retval : 0;
+}
/* ofputil_port_status */
@@ -792,6 +968,21 @@ ofputil_encode_port_status(const struct ofputil_port_status *ps,
return b;
}
+void
+ofputil_port_status_format(struct ds *s,
+ const struct ofputil_port_status *ps)
+{
+ if (ps->reason == OFPPR_ADD) {
+ ds_put_format(s, " ADD:");
+ } else if (ps->reason == OFPPR_DELETE) {
+ ds_put_format(s, " DEL:");
+ } else if (ps->reason == OFPPR_MODIFY) {
+ ds_put_format(s, " MOD:");
+ }
+
+ ofputil_phy_port_format(s, &ps->desc);
+}
+
/* ofputil_port_mod */
static enum ofperr
@@ -1011,6 +1202,33 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
return b;
}
+
+void
+ofputil_port_mod_format(struct ds *s, const struct ofputil_port_mod *pm,
+ const struct ofputil_port_map *port_map)
+{
+ ds_put_cstr(s, " port: ");
+ ofputil_format_port(pm->port_no, port_map, s);
+ ds_put_format(s, ": addr:"ETH_ADDR_FMT"\n",
+ ETH_ADDR_ARGS(pm->hw_addr));
+ if (!eth_addr64_is_zero(pm->hw_addr64)) {
+ ds_put_format(s, " addr64: "ETH_ADDR64_FMT"\n",
+ ETH_ADDR64_ARGS(pm->hw_addr64));
+ }
+
+ ds_put_cstr(s, " config: ");
+ ofputil_port_config_format(s, pm->config);
+
+ ds_put_cstr(s, " mask: ");
+ ofputil_port_config_format(s, pm->mask);
+
+ ds_put_cstr(s, " advertise: ");
+ if (pm->advertise) {
+ netdev_features_format(s, pm->advertise);
+ } else {
+ ds_put_cstr(s, "UNCHANGED\n");
+ }
+}
/* Encode a dump ports request for 'port', the encoded message
* will be for OpenFlow version 'ofp_version'. Returns message
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index c0bfa9284..096c341c9 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -117,140 +117,23 @@ ofp_dp_packet_to_string(const struct dp_packet *packet)
packet->packet_type);
}
-static void
-format_hex_arg(struct ds *s, const uint8_t *data, size_t len)
-{
- for (size_t i = 0; i < len; i++) {
- if (i) {
- ds_put_char(s, '.');
- }
- ds_put_format(s, "%02"PRIx8, data[i]);
- }
-}
-
static enum ofperr
ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
const struct ofputil_port_map *port_map,
const struct ofputil_table_map *table_map, int verbosity)
{
- char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];
struct ofputil_packet_in_private pin;
- const struct ofputil_packet_in *public = &pin.base;
uint32_t buffer_id;
size_t total_len;
- enum ofperr error;
-
- error = ofputil_decode_packet_in_private(oh, true, NULL, NULL,
- &pin, &total_len, &buffer_id);
- if (error) {
- return error;
- }
-
- if (public->table_id
- || ofputil_table_map_get_name(table_map, public->table_id)) {
- ds_put_format(string, " table_id=");
- ofputil_format_table(public->table_id, table_map, string);
- }
-
- if (public->cookie != OVS_BE64_MAX) {
- ds_put_format(string, " cookie=0x%"PRIx64, ntohll(public->cookie));
- }
-
- ds_put_format(string, " total_len=%"PRIuSIZE" ", total_len);
-
- match_format(&public->flow_metadata, port_map,
- string, OFP_DEFAULT_PRIORITY);
-
- ds_put_format(string, " (via %s)",
- ofputil_packet_in_reason_to_string(public->reason,
- reasonbuf,
- sizeof reasonbuf));
-
- ds_put_format(string, " data_len=%"PRIuSIZE, public->packet_len);
- if (buffer_id == UINT32_MAX) {
- ds_put_format(string, " (unbuffered)");
- if (total_len != public->packet_len) {
- ds_put_format(string, " (***total_len != data_len***)");
- }
- } else {
- ds_put_format(string, " buffer=0x%08"PRIx32, buffer_id);
- if (total_len < public->packet_len) {
- ds_put_format(string, " (***total_len < data_len***)");
- }
- }
- ds_put_char(string, '\n');
-
- if (public->userdata_len) {
- ds_put_cstr(string, " userdata=");
- format_hex_arg(string, pin.base.userdata, pin.base.userdata_len);
- ds_put_char(string, '\n');
- }
-
- if (!uuid_is_zero(&pin.bridge)) {
- ds_put_format(string, " continuation.bridge="UUID_FMT"\n",
- UUID_ARGS(&pin.bridge));
- }
-
- if (pin.stack_size) {
- ds_put_cstr(string, " continuation.stack=(top)");
-
- struct ofpbuf pin_stack;
- ofpbuf_use_const(&pin_stack, pin.stack, pin.stack_size);
-
- while (pin_stack.size) {
- uint8_t len;
- uint8_t *val = nx_stack_pop(&pin_stack, &len);
- union mf_subvalue value;
-
- ds_put_char(string, ' ');
- memset(&value, 0, sizeof value - len);
- memcpy(&value.u8[sizeof value - len], val, len);
- mf_subvalue_format(&value, string);
- }
- ds_put_cstr(string, " (bottom)\n");
- }
-
- if (pin.mirrors) {
- ds_put_format(string, " continuation.mirrors=0x%"PRIx32"\n",
- pin.mirrors);
- }
-
- if (pin.conntracked) {
- ds_put_cstr(string, " continuation.conntracked=true\n");
- }
-
- struct ofpact_format_params fp = {
- .port_map = port_map,
- .table_map = table_map,
- .s = string,
- };
-
- if (pin.actions_len) {
- ds_put_cstr(string, " continuation.actions=");
- ofpacts_format(pin.actions, pin.actions_len, &fp);
- ds_put_char(string, '\n');
- }
-
- if (pin.action_set_len) {
- ds_put_cstr(string, " continuation.action_set=");
- ofpacts_format(pin.action_set, pin.action_set_len, &fp);
- ds_put_char(string, '\n');
- }
-
- if (verbosity > 0) {
- char *packet = ofp_packet_to_string(
- public->packet, public->packet_len,
- public->flow_metadata.flow.packet_type);
- ds_put_cstr(string, packet);
- free(packet);
- }
- if (verbosity > 2) {
- ds_put_hex_dump(string, public->packet, public->packet_len, 0, false);
+ enum ofperr error = ofputil_decode_packet_in_private(oh, true, NULL, NULL,
+ &pin, &total_len,
+ &buffer_id);
+ if (!error) {
+ ofputil_packet_in_private_format(string, &pin, total_len, buffer_id,
+ port_map, table_map, verbosity);
+ ofputil_packet_in_private_destroy(&pin);
}
-
- ofputil_packet_in_private_destroy(&pin);
-
- return 0;
+ return error;
}
static enum ofperr
@@ -264,56 +147,14 @@ ofp_print_packet_out(struct ds *string, const struct ofp_header *oh,
ofpbuf_init(&ofpacts, 64);
error = ofputil_decode_packet_out(&po, oh, NULL, &ofpacts);
- if (error) {
- ofpbuf_uninit(&ofpacts);
- return error;
- }
-
- ds_put_char(string, ' ');
- match_format(&po.flow_metadata, port_map, string, OFP_DEFAULT_PRIORITY);
-
- ds_put_cstr(string, " actions=");
- struct ofpact_format_params fp = {
- .port_map = port_map,
- .table_map = table_map,
- .s = string,
- };
- ofpacts_format(po.ofpacts, po.ofpacts_len, &fp);
-
- if (po.buffer_id == UINT32_MAX) {
- ds_put_format(string, " data_len=%"PRIuSIZE, po.packet_len);
- if (verbosity > 0 && po.packet_len > 0) {
- ovs_be32 po_packet_type = po.flow_metadata.flow.packet_type;
- char *packet = ofp_packet_to_string(po.packet, po.packet_len,
- po_packet_type);
- ds_put_char(string, '\n');
- ds_put_cstr(string, packet);
- free(packet);
- }
- if (verbosity > 2) {
- ds_put_hex_dump(string, po.packet, po.packet_len, 0, false);
- }
- } else {
- ds_put_format(string, " buffer=0x%08"PRIx32, po.buffer_id);
+ if (!error) {
+ ofputil_packet_out_format(string, &po, port_map, table_map, verbosity);
}
-
ofpbuf_uninit(&ofpacts);
- return 0;
-}
-
-/* qsort comparison function. */
-static int
-compare_ports(const void *a_, const void *b_)
-{
- const struct ofputil_phy_port *a = a_;
- const struct ofputil_phy_port *b = b_;
- uint16_t ap = ofp_to_u16(a->port_no);
- uint16_t bp = ofp_to_u16(b->port_no);
-
- return ap < bp ? -1 : ap > bp;
+ return error;
}
-static void
+void
ofp_print_bit_names(struct ds *string, uint32_t bits,
const char *(*bit_to_name)(uint32_t bit),
char separator)
@@ -349,280 +190,17 @@ ofp_print_bit_names(struct ds *string, uint32_t bits,
}
}
-static const char *
-netdev_feature_to_name(uint32_t bit)
-{
- enum netdev_features f = bit;
-
- switch (f) {
- case NETDEV_F_10MB_HD: return "10MB-HD";
- case NETDEV_F_10MB_FD: return "10MB-FD";
- case NETDEV_F_100MB_HD: return "100MB-HD";
- case NETDEV_F_100MB_FD: return "100MB-FD";
- case NETDEV_F_1GB_HD: return "1GB-HD";
- case NETDEV_F_1GB_FD: return "1GB-FD";
- case NETDEV_F_10GB_FD: return "10GB-FD";
- case NETDEV_F_40GB_FD: return "40GB-FD";
- case NETDEV_F_100GB_FD: return "100GB-FD";
- case NETDEV_F_1TB_FD: return "1TB-FD";
- case NETDEV_F_OTHER: return "OTHER";
- case NETDEV_F_COPPER: return "COPPER";
- case NETDEV_F_FIBER: return "FIBER";
- case NETDEV_F_AUTONEG: return "AUTO_NEG";
- case NETDEV_F_PAUSE: return "AUTO_PAUSE";
- case NETDEV_F_PAUSE_ASYM: return "AUTO_PAUSE_ASYM";
- }
-
- return NULL;
-}
-
-static void
-ofp_print_port_features(struct ds *string, enum netdev_features features)
-{
- ofp_print_bit_names(string, features, netdev_feature_to_name, ' ');
- ds_put_char(string, '\n');
-}
-
-static const char *
-ofputil_port_config_to_name(uint32_t bit)
-{
- enum ofputil_port_config pc = bit;
-
- switch (pc) {
- case OFPUTIL_PC_PORT_DOWN: return "PORT_DOWN";
- case OFPUTIL_PC_NO_STP: return "NO_STP";
- case OFPUTIL_PC_NO_RECV: return "NO_RECV";
- case OFPUTIL_PC_NO_RECV_STP: return "NO_RECV_STP";
- case OFPUTIL_PC_NO_FLOOD: return "NO_FLOOD";
- case OFPUTIL_PC_NO_FWD: return "NO_FWD";
- case OFPUTIL_PC_NO_PACKET_IN: return "NO_PACKET_IN";
- }
-
- return NULL;
-}
-
-static void
-ofp_print_port_config(struct ds *string, enum ofputil_port_config config)
-{
- ofp_print_bit_names(string, config, ofputil_port_config_to_name, ' ');
- ds_put_char(string, '\n');
-}
-
-static const char *
-ofputil_port_state_to_name(uint32_t bit)
-{
- enum ofputil_port_state ps = bit;
-
- switch (ps) {
- case OFPUTIL_PS_LINK_DOWN: return "LINK_DOWN";
- case OFPUTIL_PS_BLOCKED: return "BLOCKED";
- case OFPUTIL_PS_LIVE: return "LIVE";
-
- case OFPUTIL_PS_STP_LISTEN:
- case OFPUTIL_PS_STP_LEARN:
- case OFPUTIL_PS_STP_FORWARD:
- case OFPUTIL_PS_STP_BLOCK:
- /* Handled elsewhere. */
- return NULL;
- }
-
- return NULL;
-}
-
-static void
-ofp_print_port_state(struct ds *string, enum ofputil_port_state state)
-{
- enum ofputil_port_state stp_state;
-
- /* The STP state is a 2-bit field so it doesn't fit in with the bitmask
- * pattern. We have to special case it.
- *
- * OVS doesn't support STP, so this field will always be 0 if we are
- * talking to OVS, so we'd always print STP_LISTEN in that case.
- * Therefore, we don't print anything at all if the value is STP_LISTEN, to
- * avoid confusing users. */
- stp_state = state & OFPUTIL_PS_STP_MASK;
- if (stp_state) {
- ds_put_cstr(string,
- (stp_state == OFPUTIL_PS_STP_LEARN ? "STP_LEARN"
- : stp_state == OFPUTIL_PS_STP_FORWARD ? "STP_FORWARD"
- : "STP_BLOCK"));
- state &= ~OFPUTIL_PS_STP_MASK;
- if (state) {
- ofp_print_bit_names(string, state, ofputil_port_state_to_name,
- ' ');
- }
- } else {
- ofp_print_bit_names(string, state, ofputil_port_state_to_name, ' ');
- }
- ds_put_char(string, '\n');
-}
-
-static void
-ofp_print_phy_port(struct ds *string, const struct ofputil_phy_port *port)
-{
- char name[sizeof port->name];
- int j;
-
- memcpy(name, port->name, sizeof name);
- for (j = 0; j < sizeof name - 1; j++) {
- if (!isprint((unsigned char) name[j])) {
- break;
- }
- }
- name[j] = '\0';
-
- ds_put_char(string, ' ');
- ofputil_format_port(port->port_no, NULL, string);
- ds_put_format(string, "(%s): addr:"ETH_ADDR_FMT"\n",
- name, ETH_ADDR_ARGS(port->hw_addr));
-
- if (!eth_addr64_is_zero(port->hw_addr64)) {
- ds_put_format(string, " addr64: "ETH_ADDR64_FMT"\n",
- ETH_ADDR64_ARGS(port->hw_addr64));
- }
-
- ds_put_cstr(string, " config: ");
- ofp_print_port_config(string, port->config);
-
- ds_put_cstr(string, " state: ");
- ofp_print_port_state(string, port->state);
-
- if (port->curr) {
- ds_put_format(string, " current: ");
- ofp_print_port_features(string, port->curr);
- }
- if (port->advertised) {
- ds_put_format(string, " advertised: ");
- ofp_print_port_features(string, port->advertised);
- }
- if (port->supported) {
- ds_put_format(string, " supported: ");
- ofp_print_port_features(string, port->supported);
- }
- if (port->peer) {
- ds_put_format(string, " peer: ");
- ofp_print_port_features(string, port->peer);
- }
- ds_put_format(string, " speed: %"PRIu32" Mbps now, "
- "%"PRIu32" Mbps max\n",
- port->curr_speed / UINT32_C(1000),
- port->max_speed / UINT32_C(1000));
-}
-
-/* Given a buffer 'b' that contains an array of OpenFlow ports of type
- * 'ofp_version', writes a detailed description of each port into
- * 'string'. */
-static enum ofperr
-ofp_print_phy_ports(struct ds *string, uint8_t ofp_version,
- struct ofpbuf *b)
-{
- struct ofputil_phy_port *ports;
- size_t allocated_ports, n_ports;
- int retval;
- size_t i;
-
- ports = NULL;
- allocated_ports = 0;
- for (n_ports = 0; ; n_ports++) {
- if (n_ports >= allocated_ports) {
- ports = x2nrealloc(ports, &allocated_ports, sizeof *ports);
- }
-
- retval = ofputil_pull_phy_port(ofp_version, b, &ports[n_ports]);
- if (retval) {
- break;
- }
- }
-
- qsort(ports, n_ports, sizeof *ports, compare_ports);
- for (i = 0; i < n_ports; i++) {
- ofp_print_phy_port(string, &ports[i]);
- }
- free(ports);
-
- return retval != EOF ? retval : 0;
-}
-
-static const char *
-ofputil_capabilities_to_name(uint32_t bit)
-{
- enum ofputil_capabilities capabilities = bit;
-
- switch (capabilities) {
- case OFPUTIL_C_FLOW_STATS: return "FLOW_STATS";
- case OFPUTIL_C_TABLE_STATS: return "TABLE_STATS";
- case OFPUTIL_C_PORT_STATS: return "PORT_STATS";
- case OFPUTIL_C_IP_REASM: return "IP_REASM";
- case OFPUTIL_C_QUEUE_STATS: return "QUEUE_STATS";
- case OFPUTIL_C_ARP_MATCH_IP: return "ARP_MATCH_IP";
- case OFPUTIL_C_STP: return "STP";
- case OFPUTIL_C_GROUP_STATS: return "GROUP_STATS";
- case OFPUTIL_C_PORT_BLOCKED: return "PORT_BLOCKED";
- case OFPUTIL_C_BUNDLES: return "BUNDLES";
- case OFPUTIL_C_FLOW_MONITORING: return "FLOW_MONITORING";
- }
-
- return NULL;
-}
-
static enum ofperr
ofp_print_switch_features(struct ds *string, const struct ofp_header *oh)
{
struct ofputil_switch_features features;
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
enum ofperr error = ofputil_pull_switch_features(&b, &features);
- if (error) {
- return error;
- }
-
- ds_put_format(string, " dpid:%016"PRIx64"\n", features.datapath_id);
-
- ds_put_format(string, "n_tables:%"PRIu8", n_buffers:%"PRIu32,
- features.n_tables, features.n_buffers);
- if (features.auxiliary_id) {
- ds_put_format(string, ", auxiliary_id:%"PRIu8, features.auxiliary_id);
- }
- ds_put_char(string, '\n');
-
- ds_put_cstr(string, "capabilities: ");
- ofp_print_bit_names(string, features.capabilities,
- ofputil_capabilities_to_name, ' ');
- ds_put_char(string, '\n');
-
- switch ((enum ofp_version)oh->version) {
- case OFP10_VERSION:
- ds_put_cstr(string, "actions: ");
- ofpact_bitmap_format(features.ofpacts, string);
- ds_put_char(string, '\n');
- break;
- case OFP11_VERSION:
- case OFP12_VERSION:
- break;
- case OFP13_VERSION:
- case OFP14_VERSION:
- case OFP15_VERSION:
- case OFP16_VERSION:
- return 0; /* no ports in ofp13_switch_features */
- default:
- OVS_NOT_REACHED();
- }
-
- return ofp_print_phy_ports(string, oh->version, &b);
-}
-
-static void
-ofp_print_switch_config(struct ds *string,
- const struct ofputil_switch_config *config)
-{
- ds_put_format(string, " frags=%s",
- ofputil_frag_handling_to_string(config->frag));
-
- if (config->invalid_ttl_to_controller > 0) {
- ds_put_format(string, " invalid_ttl_to_controller");
+ if (!error) {
+ ofputil_switch_features_format(string, &features);
+ error = ofputil_phy_ports_format(string, oh->version, &b);
}
-
- ds_put_format(string, " miss_send_len=%"PRIu16"\n", config->miss_send_len);
+ return error;
}
static enum ofperr
@@ -635,7 +213,7 @@ ofp_print_set_config(struct ds *string, const struct ofp_header *oh)
if (error) {
return error;
}
- ofp_print_switch_config(string, &config);
+ ofputil_switch_config_format(string, &config);
return 0;
}
@@ -644,319 +222,34 @@ ofp_print_get_config_reply(struct ds *string, const struct ofp_header *oh)
{
struct ofputil_switch_config config;
ofputil_decode_get_config_reply(oh, &config);
- ofp_print_switch_config(string, &config);
+ ofputil_switch_config_format(string, &config);
return 0;
}
-static void print_wild(struct ds *string, const char *leader, int is_wild,
- int verbosity, const char *format, ...)
- OVS_PRINTF_FORMAT(5, 6);
-
-static void print_wild(struct ds *string, const char *leader, int is_wild,
- int verbosity, const char *format, ...)
-{
- if (is_wild && verbosity < 2) {
- return;
- }
- ds_put_cstr(string, leader);
- if (!is_wild) {
- va_list args;
-
- va_start(args, format);
- ds_put_format_valist(string, format, args);
- va_end(args);
- } else {
- ds_put_char(string, '*');
- }
- ds_put_char(string, ',');
-}
-
-static void
-print_wild_port(struct ds *string, const char *leader, int is_wild,
- int verbosity, ofp_port_t port,
- const struct ofputil_port_map *port_map)
-{
- if (is_wild && verbosity < 2) {
- return;
- }
- ds_put_cstr(string, leader);
- if (!is_wild) {
- ofputil_format_port(port, port_map, string);
- } else {
- ds_put_char(string, '*');
- }
- ds_put_char(string, ',');
-}
-
-static void
-print_ip_netmask(struct ds *string, const char *leader, ovs_be32 ip,
- uint32_t wild_bits, int verbosity)
-{
- if (wild_bits >= 32 && verbosity < 2) {
- return;
- }
- ds_put_cstr(string, leader);
- if (wild_bits < 32) {
- ds_put_format(string, IP_FMT, IP_ARGS(ip));
- if (wild_bits) {
- ds_put_format(string, "/%d", 32 - wild_bits);
- }
- } else {
- ds_put_char(string, '*');
- }
- ds_put_char(string, ',');
-}
-
-void
-ofp10_match_print(struct ds *f, const struct ofp10_match *om,
- const struct ofputil_port_map *port_map, int verbosity)
-{
- char *s = ofp10_match_to_string(om, port_map, verbosity);
- ds_put_cstr(f, s);
- free(s);
-}
-
-char *
-ofp10_match_to_string(const struct ofp10_match *om,
- const struct ofputil_port_map *port_map, int verbosity)
-{
- struct ds f = DS_EMPTY_INITIALIZER;
- uint32_t w = ntohl(om->wildcards);
- bool skip_type = false;
- bool skip_proto = false;
-
- if (!(w & OFPFW10_DL_TYPE)) {
- skip_type = true;
- if (om->dl_type == htons(ETH_TYPE_IP)) {
- if (!(w & OFPFW10_NW_PROTO)) {
- skip_proto = true;
- if (om->nw_proto == IPPROTO_ICMP) {
- ds_put_cstr(&f, "icmp,");
- } else if (om->nw_proto == IPPROTO_TCP) {
- ds_put_cstr(&f, "tcp,");
- } else if (om->nw_proto == IPPROTO_UDP) {
- ds_put_cstr(&f, "udp,");
- } else if (om->nw_proto == IPPROTO_SCTP) {
- ds_put_cstr(&f, "sctp,");
- } else {
- ds_put_cstr(&f, "ip,");
- skip_proto = false;
- }
- } else {
- ds_put_cstr(&f, "ip,");
- }
- } else if (om->dl_type == htons(ETH_TYPE_ARP)) {
- ds_put_cstr(&f, "arp,");
- } else if (om->dl_type == htons(ETH_TYPE_RARP)){
- ds_put_cstr(&f, "rarp,");
- } else if (om->dl_type == htons(ETH_TYPE_MPLS)) {
- ds_put_cstr(&f, "mpls,");
- } else if (om->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
- ds_put_cstr(&f, "mplsm,");
- } else {
- skip_type = false;
- }
- }
- print_wild_port(&f, "in_port=", w & OFPFW10_IN_PORT, verbosity,
- u16_to_ofp(ntohs(om->in_port)), port_map);
- print_wild(&f, "dl_vlan=", w & OFPFW10_DL_VLAN, verbosity,
- "%d", ntohs(om->dl_vlan));
- print_wild(&f, "dl_vlan_pcp=", w & OFPFW10_DL_VLAN_PCP, verbosity,
- "%d", om->dl_vlan_pcp);
- print_wild(&f, "dl_src=", w & OFPFW10_DL_SRC, verbosity,
- ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_src));
- print_wild(&f, "dl_dst=", w & OFPFW10_DL_DST, verbosity,
- ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_dst));
- if (!skip_type) {
- print_wild(&f, "dl_type=", w & OFPFW10_DL_TYPE, verbosity,
- "0x%04x", ntohs(om->dl_type));
- }
- print_ip_netmask(&f, "nw_src=", om->nw_src,
- (w & OFPFW10_NW_SRC_MASK) >> OFPFW10_NW_SRC_SHIFT,
- verbosity);
- print_ip_netmask(&f, "nw_dst=", om->nw_dst,
- (w & OFPFW10_NW_DST_MASK) >> OFPFW10_NW_DST_SHIFT,
- verbosity);
- if (!skip_proto) {
- if (om->dl_type == htons(ETH_TYPE_ARP) ||
- om->dl_type == htons(ETH_TYPE_RARP)) {
- print_wild(&f, "arp_op=", w & OFPFW10_NW_PROTO, verbosity,
- "%u", om->nw_proto);
- } else {
- print_wild(&f, "nw_proto=", w & OFPFW10_NW_PROTO, verbosity,
- "%u", om->nw_proto);
- }
- }
- print_wild(&f, "nw_tos=", w & OFPFW10_NW_TOS, verbosity,
- "%u", om->nw_tos);
- if (om->nw_proto == IPPROTO_ICMP) {
- print_wild(&f, "icmp_type=", w & OFPFW10_ICMP_TYPE, verbosity,
- "%d", ntohs(om->tp_src));
- print_wild(&f, "icmp_code=", w & OFPFW10_ICMP_CODE, verbosity,
- "%d", ntohs(om->tp_dst));
- } else {
- print_wild(&f, "tp_src=", w & OFPFW10_TP_SRC, verbosity,
- "%d", ntohs(om->tp_src));
- print_wild(&f, "tp_dst=", w & OFPFW10_TP_DST, verbosity,
- "%d", ntohs(om->tp_dst));
- }
- ds_chomp(&f, ',');
- return ds_cstr(&f);
-}
-
-static void
-ofp_print_flow_flags(struct ds *s, enum ofputil_flow_mod_flags flags)
-{
- if (flags & OFPUTIL_FF_SEND_FLOW_REM) {
- ds_put_cstr(s, "send_flow_rem ");
- }
- if (flags & OFPUTIL_FF_CHECK_OVERLAP) {
- ds_put_cstr(s, "check_overlap ");
- }
- if (flags & OFPUTIL_FF_RESET_COUNTS) {
- ds_put_cstr(s, "reset_counts ");
- }
- if (flags & OFPUTIL_FF_NO_PKT_COUNTS) {
- ds_put_cstr(s, "no_packet_counts ");
- }
- if (flags & OFPUTIL_FF_NO_BYT_COUNTS) {
- ds_put_cstr(s, "no_byte_counts ");
- }
- if (flags & OFPUTIL_FF_HIDDEN_FIELDS) {
- ds_put_cstr(s, "allow_hidden_fields ");
- }
- if (flags & OFPUTIL_FF_NO_READONLY) {
- ds_put_cstr(s, "no_readonly_table ");
- }
-}
-
static enum ofperr
-ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
- const struct ofputil_port_map *port_map,
- const struct ofputil_table_map *table_map, int verbosity)
+ofp_print_table_features_reply(struct ds *s, const struct ofp_header *oh,
+ const struct ofputil_table_map *table_map)
{
- struct ofputil_flow_mod fm;
- struct ofpbuf ofpacts;
- bool need_priority;
- enum ofperr error;
- enum ofpraw raw;
- enum ofputil_protocol protocol;
-
- protocol = ofputil_protocol_from_ofp_version(oh->version);
- protocol = ofputil_protocol_set_tid(protocol, true);
-
- ofpbuf_init(&ofpacts, 64);
- error = ofputil_decode_flow_mod(&fm, oh, protocol, NULL, NULL, &ofpacts,
- OFPP_MAX, 255);
- if (error) {
- ofpbuf_uninit(&ofpacts);
- return error;
- }
-
- ds_put_char(s, ' ');
- switch (fm.command) {
- case OFPFC_ADD:
- ds_put_cstr(s, "ADD");
- break;
- case OFPFC_MODIFY:
- ds_put_cstr(s, "MOD");
- break;
- case OFPFC_MODIFY_STRICT:
- ds_put_cstr(s, "MOD_STRICT");
- break;
- case OFPFC_DELETE:
- ds_put_cstr(s, "DEL");
- break;
- case OFPFC_DELETE_STRICT:
- ds_put_cstr(s, "DEL_STRICT");
- break;
- default:
- ds_put_format(s, "cmd:%d", fm.command);
- }
- if (fm.table_id != 0
- || ofputil_table_map_get_name(table_map, fm.table_id)) {
- ds_put_format(s, " table:");
- ofputil_format_table(fm.table_id, table_map, s);
- }
-
- ds_put_char(s, ' ');
- ofpraw_decode(&raw, oh);
- if (verbosity >= 3 && raw == OFPRAW_OFPT10_FLOW_MOD) {
- const struct ofp10_flow_mod *ofm = ofpmsg_body(oh);
- ofp10_match_print(s, &ofm->match, port_map, verbosity);
-
- /* ofp_print_match() doesn't print priority. */
- need_priority = true;
- } else if (verbosity >= 3 && raw == OFPRAW_NXT_FLOW_MOD) {
- const struct nx_flow_mod *nfm = ofpmsg_body(oh);
- const void *nxm = nfm + 1;
- char *nxm_s;
-
- nxm_s = nx_match_to_string(nxm, ntohs(nfm->match_len));
- ds_put_cstr(s, nxm_s);
- free(nxm_s);
-
- /* nx_match_to_string() doesn't print priority. */
- need_priority = true;
- } else {
- match_format(&fm.match, port_map, s, fm.priority);
+ struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
- /* match_format() does print priority. */
- need_priority = false;
- }
+ struct ofputil_table_features prev;
+ for (int i = 0; ; i++) {
+ struct ofputil_table_features tf;
+ int retval;
- if (ds_last(s) != ' ') {
- ds_put_char(s, ' ');
- }
- if (fm.new_cookie != htonll(0) && fm.new_cookie != OVS_BE64_MAX) {
- ds_put_format(s, "cookie:0x%"PRIx64" ", ntohll(fm.new_cookie));
- }
- if (fm.cookie_mask != htonll(0)) {
- ds_put_format(s, "cookie:0x%"PRIx64"/0x%"PRIx64" ",
- ntohll(fm.cookie), ntohll(fm.cookie_mask));
- }
- if (fm.idle_timeout != OFP_FLOW_PERMANENT) {
- ds_put_format(s, "idle:%"PRIu16" ", fm.idle_timeout);
- }
- if (fm.hard_timeout != OFP_FLOW_PERMANENT) {
- ds_put_format(s, "hard:%"PRIu16" ", fm.hard_timeout);
- }
- if (fm.importance != 0) {
- ds_put_format(s, "importance:%"PRIu16" ", fm.importance);
- }
- if (fm.priority != OFP_DEFAULT_PRIORITY && need_priority) {
- ds_put_format(s, "pri:%d ", fm.priority);
- }
- if (fm.buffer_id != UINT32_MAX) {
- ds_put_format(s, "buf:0x%"PRIx32" ", fm.buffer_id);
- }
- if (fm.out_port != OFPP_ANY) {
- ds_put_format(s, "out_port:");
- ofputil_format_port(fm.out_port, port_map, s);
- ds_put_char(s, ' ');
- }
+ retval = ofputil_decode_table_features(&b, &tf, true);
+ if (retval) {
+ return retval != EOF ? retval : 0;
+ }
- if (oh->version == OFP10_VERSION || oh->version == OFP11_VERSION) {
- /* Don't print the reset_counts flag for OF1.0 and OF1.1 because those
- * versions don't really have such a flag and printing one is likely to
- * confuse people. */
- fm.flags &= ~OFPUTIL_FF_RESET_COUNTS;
+ ds_put_char(s, '\n');
+ ofputil_table_features_format(s, &tf, i ? &prev : NULL, NULL, NULL,
+ table_map);
+ prev = tf;
}
- ofp_print_flow_flags(s, fm.flags);
-
- ds_put_cstr(s, "actions=");
- struct ofpact_format_params fp = {
- .port_map = port_map,
- .table_map = table_map,
- .s = s,
- };
- ofpacts_format(fm.ofpacts, fm.ofpacts_len, &fp);
- ofpbuf_uninit(&ofpacts);
-
- return 0;
}
-static void
+void
ofp_print_duration(struct ds *string, unsigned int sec, unsigned int nsec)
{
ds_put_format(string, "%u", sec);
@@ -985,74 +278,17 @@ ofp_print_duration(struct ds *string, unsigned int sec, unsigned int nsec)
ds_put_char(string, 's');
}
-/* Returns a string form of 'reason'. The return value is either a statically
- * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'.
- * 'bufsize' should be at least OFP_FLOW_REMOVED_REASON_BUFSIZE. */
-#define OFP_FLOW_REMOVED_REASON_BUFSIZE (INT_STRLEN(int) + 1)
-static const char *
-ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason,
- char *reasonbuf, size_t bufsize)
-{
- switch (reason) {
- case OFPRR_IDLE_TIMEOUT:
- return "idle";
- case OFPRR_HARD_TIMEOUT:
- return "hard";
- case OFPRR_DELETE:
- return "delete";
- case OFPRR_GROUP_DELETE:
- return "group_delete";
- case OFPRR_EVICTION:
- return "eviction";
- case OFPRR_METER_DELETE:
- return "meter_delete";
- case OVS_OFPRR_NONE:
- default:
- snprintf(reasonbuf, bufsize, "%d", (int) reason);
- return reasonbuf;
- }
-}
-
static enum ofperr
ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh,
const struct ofputil_port_map *port_map,
const struct ofputil_table_map *table_map)
{
- char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE];
struct ofputil_flow_removed fr;
- enum ofperr error;
-
- error = ofputil_decode_flow_removed(&fr, oh);
- if (error) {
- return error;
- }
-
- ds_put_char(string, ' ');
- match_format(&fr.match, port_map, string, fr.priority);
-
- ds_put_format(string, " reason=%s",
- ofp_flow_removed_reason_to_string(fr.reason, reasonbuf,
- sizeof reasonbuf));
-
- if (fr.table_id != 255) {
- ds_put_format(string, " table_id=");
- ofputil_format_table(fr.table_id, table_map, string);
- }
-
- if (fr.cookie != htonll(0)) {
- ds_put_format(string, " cookie:0x%"PRIx64, ntohll(fr.cookie));
- }
- ds_put_cstr(string, " duration");
- ofp_print_duration(string, fr.duration_sec, fr.duration_nsec);
- ds_put_format(string, " idle%"PRIu16, fr.idle_timeout);
- if (fr.hard_timeout) {
- /* The hard timeout was only added in OF1.2, so only print it if it is
- * actually in use to avoid gratuitous change to the formatting. */
- ds_put_format(string, " hard%"PRIu16, fr.hard_timeout);
+ enum ofperr error = ofputil_decode_flow_removed(&fr, oh);
+ if (!error) {
+ ofputil_flow_removed_format(string, &fr, port_map, table_map);
}
- ds_put_format(string, " pkts%"PRIu64" bytes%"PRIu64"\n",
- fr.packet_count, fr.byte_count);
- return 0;
+ return error;
}
static enum ofperr
@@ -1060,168 +296,23 @@ ofp_print_port_mod(struct ds *string, const struct ofp_header *oh,
const struct ofputil_port_map *port_map)
{
struct ofputil_port_mod pm;
- enum ofperr error;
-
- error = ofputil_decode_port_mod(oh, &pm, true);
- if (error) {
- return error;
- }
-
- ds_put_cstr(string, " port: ");
- ofputil_format_port(pm.port_no, port_map, string);
- ds_put_format(string, ": addr:"ETH_ADDR_FMT"\n",
- ETH_ADDR_ARGS(pm.hw_addr));
- if (!eth_addr64_is_zero(pm.hw_addr64)) {
- ds_put_format(string, " addr64: "ETH_ADDR64_FMT"\n",
- ETH_ADDR64_ARGS(pm.hw_addr64));
- }
-
- ds_put_cstr(string, " config: ");
- ofp_print_port_config(string, pm.config);
-
- ds_put_cstr(string, " mask: ");
- ofp_print_port_config(string, pm.mask);
-
- ds_put_cstr(string, " advertise: ");
- if (pm.advertise) {
- ofp_print_port_features(string, pm.advertise);
- } else {
- ds_put_cstr(string, "UNCHANGED\n");
- }
-
- return 0;
-}
-
-static const char *
-ofputil_table_miss_to_string(enum ofputil_table_miss miss)
-{
- switch (miss) {
- case OFPUTIL_TABLE_MISS_DEFAULT: return "default";
- case OFPUTIL_TABLE_MISS_CONTROLLER: return "controller";
- case OFPUTIL_TABLE_MISS_CONTINUE: return "continue";
- case OFPUTIL_TABLE_MISS_DROP: return "drop";
- default: return "***error***";
- }
-}
-
-static const char *
-ofputil_table_eviction_to_string(enum ofputil_table_eviction eviction)
-{
- switch (eviction) {
- case OFPUTIL_TABLE_EVICTION_DEFAULT: return "default";
- case OFPUTIL_TABLE_EVICTION_ON: return "on";
- case OFPUTIL_TABLE_EVICTION_OFF: return "off";
- default: return "***error***";
- }
-
-}
-
-static const char *
-ofputil_eviction_flag_to_string(uint32_t bit)
-{
- enum ofp14_table_mod_prop_eviction_flag eviction_flag = bit;
-
- switch (eviction_flag) {
- case OFPTMPEF14_OTHER: return "OTHER";
- case OFPTMPEF14_IMPORTANCE: return "IMPORTANCE";
- case OFPTMPEF14_LIFETIME: return "LIFETIME";
- }
-
- return NULL;
-}
-
-/* Appends to 'string' a description of the bitmap of OFPTMPEF14_* values in
- * 'eviction_flags'. */
-static void
-ofputil_put_eviction_flags(struct ds *string, uint32_t eviction_flags)
-{
- if (eviction_flags != UINT32_MAX) {
- ofp_print_bit_names(string, eviction_flags,
- ofputil_eviction_flag_to_string, '|');
- } else {
- ds_put_cstr(string, "(default)");
- }
-}
-
-static const char *
-ofputil_table_vacancy_to_string(enum ofputil_table_vacancy vacancy)
-{
- switch (vacancy) {
- case OFPUTIL_TABLE_VACANCY_DEFAULT: return "default";
- case OFPUTIL_TABLE_VACANCY_ON: return "on";
- case OFPUTIL_TABLE_VACANCY_OFF: return "off";
- default: return "***error***";
+ enum ofperr error = ofputil_decode_port_mod(oh, &pm, true);
+ if (!error) {
+ ofputil_port_mod_format(string, &pm, port_map);
}
-
+ return error;
}
static enum ofperr
ofp_print_table_mod(struct ds *string, const struct ofp_header *oh,
const struct ofputil_table_map *table_map)
{
- struct ofputil_table_mod pm;
- enum ofperr error;
-
- error = ofputil_decode_table_mod(oh, &pm);
- if (error) {
- return error;
- }
-
- if (pm.table_id == 0xff) {
- ds_put_cstr(string, " table_id: ALL_TABLES");
- } else {
- ds_put_format(string, " table_id=");
- ofputil_format_table(pm.table_id, table_map, string);
- }
-
- if (pm.miss != OFPUTIL_TABLE_MISS_DEFAULT) {
- ds_put_format(string, ", flow_miss_config=%s",
- ofputil_table_miss_to_string(pm.miss));
- }
- if (pm.eviction != OFPUTIL_TABLE_EVICTION_DEFAULT) {
- ds_put_format(string, ", eviction=%s",
- ofputil_table_eviction_to_string(pm.eviction));
- }
- if (pm.eviction_flags != UINT32_MAX) {
- ds_put_cstr(string, "eviction_flags=");
- ofputil_put_eviction_flags(string, pm.eviction_flags);
- }
- if (pm.vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) {
- ds_put_format(string, ", vacancy=%s",
- ofputil_table_vacancy_to_string(pm.vacancy));
- if (pm.vacancy == OFPUTIL_TABLE_VACANCY_ON) {
- ds_put_format(string, " vacancy:%"PRIu8""
- ",%"PRIu8"", pm.table_vacancy.vacancy_down,
- pm.table_vacancy.vacancy_up);
- }
- }
-
- return 0;
-}
-
-/* This function will print the Table description properties. */
-static void
-ofp_print_table_desc(struct ds *string, const struct ofputil_table_desc *td,
- const struct ofputil_table_map *table_map)
-{
- ds_put_format(string, "\n table ");
- ofputil_format_table(td->table_id, table_map, string);
- ds_put_cstr(string, ":\n");
- ds_put_format(string, " eviction=%s eviction_flags=",
- ofputil_table_eviction_to_string(td->eviction));
- ofputil_put_eviction_flags(string, td->eviction_flags);
- ds_put_char(string, '\n');
- ds_put_format(string, " vacancy=%s",
- ofputil_table_vacancy_to_string(td->vacancy));
- if (td->vacancy == OFPUTIL_TABLE_VACANCY_ON) {
- ds_put_format(string, " vacancy_down=%"PRIu8"%%",
- td->table_vacancy.vacancy_down);
- ds_put_format(string, " vacancy_up=%"PRIu8"%%",
- td->table_vacancy.vacancy_up);
- ds_put_format(string, " vacancy=%"PRIu8"%%",
- td->table_vacancy.vacancy);
+ struct ofputil_table_mod tm;
+ enum ofperr error = ofputil_decode_table_mod(oh, &tm);
+ if (!error) {
+ ofputil_table_mod_format(string, &tm, table_map);
}
- ds_put_char(string, '\n');
+ return error;
}
static enum ofperr
@@ -1243,7 +334,7 @@ ofp_print_table_status_message(struct ds *string, const struct ofp_header *oh,
}
ds_put_format(string, "\ntable_desc:-");
- ofp_print_table_desc(string, &ts.desc, table_map);
+ ofputil_table_desc_format(string, &ts.desc, table_map);
return 0;
}
@@ -1616,19 +707,7 @@ ofp_print_error(struct ds *string, enum ofperr error)
static enum ofperr
ofp_print_hello(struct ds *string, const struct ofp_header *oh)
{
- uint32_t allowed_versions;
- bool ok;
-
- ok = ofputil_decode_hello(oh, &allowed_versions);
-
- ds_put_cstr(string, "\n version bitmap: ");
- ofputil_format_version_bitmap(string, allowed_versions);
-
- if (!ok) {
- ds_put_cstr(string, "\n unknown data in hello:\n");
- ds_put_hex_dump(string, oh, ntohs(oh->length), 0, true);
- }
-
+ ofputil_hello_format(string, oh);
return 0;
}
@@ -1638,23 +717,11 @@ ofp_print_error_msg(struct ds *string, const struct ofp_header *oh,
const struct ofputil_table_map *table_map)
{
struct ofpbuf payload;
- enum ofperr error;
- char *s;
-
- error = ofperr_decode_msg(oh, &payload);
+ enum ofperr error = ofperr_decode_msg(oh, &payload);
if (!error) {
return OFPERR_OFPBRC_BAD_LEN;
}
-
- ds_put_format(string, " %s\n", ofperr_get_name(error));
-
- if (error == OFPERR_OFPHFC_INCOMPATIBLE || error == OFPERR_OFPHFC_EPERM) {
- ds_put_printable(string, payload.data, payload.size);
- } else {
- s = ofp_to_string(payload.data, payload.size, port_map, table_map, 1);
- ds_put_cstr(string, s);
- free(s);
- }
+ ofperr_msg_format(string, error, &payload, port_map, table_map);
ofpbuf_uninit(&payload);
return 0;
@@ -1664,23 +731,11 @@ static enum ofperr
ofp_print_port_status(struct ds *string, const struct ofp_header *oh)
{
struct ofputil_port_status ps;
- enum ofperr error;
-
- error = ofputil_decode_port_status(oh, &ps);
- if (error) {
- return error;
- }
-
- if (ps.reason == OFPPR_ADD) {
- ds_put_format(string, " ADD:");
- } else if (ps.reason == OFPPR_DELETE) {
- ds_put_format(string, " DEL:");
- } else if (ps.reason == OFPPR_MODIFY) {
- ds_put_format(string, " MOD:");
+ enum ofperr error = ofputil_decode_port_status(oh, &ps);
+ if (!error) {
+ ofputil_port_status_format(string, &ps);
}
-
- ofp_print_phy_port(string, &ps.desc);
- return 0;
+ return error;
}
static enum ofperr
@@ -1709,100 +764,12 @@ ofp_print_flow_stats_request(struct ds *string, const struct ofp_header *oh,
const struct ofputil_table_map *table_map)
{
struct ofputil_flow_stats_request fsr;
- enum ofperr error;
-
- error = ofputil_decode_flow_stats_request(&fsr, oh, NULL, NULL);
- if (error) {
- return error;
- }
-
- if (fsr.table_id != 0xff) {
- ds_put_format(string, " table=");
- ofputil_format_table(fsr.table_id, table_map, string);
- }
-
- if (fsr.out_port != OFPP_ANY) {
- ds_put_cstr(string, " out_port=");
- ofputil_format_port(fsr.out_port, port_map, string);
- }
-
- ds_put_char(string, ' ');
- match_format(&fsr.match, port_map, string, OFP_DEFAULT_PRIORITY);
-
- return 0;
-}
-
-/* Appends a textual form of 'fs' to 'string', translating port numbers to
- * names using 'port_map' (if provided). If 'show_stats' is true, the output
- * includes the flow duration, packet and byte counts, and its idle and hard
- * ages, otherwise they are omitted. */
-void
-ofp_print_flow_stats(struct ds *string, const struct ofputil_flow_stats *fs,
- const struct ofputil_port_map *port_map,
- const struct ofputil_table_map *table_map,
- bool show_stats)
-{
- if (show_stats || fs->cookie) {
- ds_put_format(string, "%scookie=%s0x%"PRIx64", ",
- colors.param, colors.end, ntohll(fs->cookie));
- }
- if (show_stats) {
- ds_put_format(string, "%sduration=%s", colors.param, colors.end);
- ofp_print_duration(string, fs->duration_sec, fs->duration_nsec);
- ds_put_cstr(string, ", ");
- }
-
- if (show_stats || fs->table_id
- || ofputil_table_map_get_name(table_map, fs->table_id) != NULL) {
- ds_put_format(string, "%stable=%s", colors.special, colors.end);
- ofputil_format_table(fs->table_id, table_map, string);
- ds_put_cstr(string, ", ");
- }
- if (show_stats) {
- ds_put_format(string, "%sn_packets=%s%"PRIu64", ",
- colors.param, colors.end, fs->packet_count);
- ds_put_format(string, "%sn_bytes=%s%"PRIu64", ",
- colors.param, colors.end, fs->byte_count);
- }
- if (fs->idle_timeout != OFP_FLOW_PERMANENT) {
- ds_put_format(string, "%sidle_timeout=%s%"PRIu16", ",
- colors.param, colors.end, fs->idle_timeout);
- }
- if (fs->hard_timeout != OFP_FLOW_PERMANENT) {
- ds_put_format(string, "%shard_timeout=%s%"PRIu16", ",
- colors.param, colors.end, fs->hard_timeout);
- }
- if (fs->flags) {
- ofp_print_flow_flags(string, fs->flags);
- }
- if (fs->importance != 0) {
- ds_put_format(string, "%simportance=%s%"PRIu16", ",
- colors.param, colors.end, fs->importance);
- }
- if (show_stats && fs->idle_age >= 0) {
- ds_put_format(string, "%sidle_age=%s%d, ",
- colors.param, colors.end, fs->idle_age);
- }
- if (show_stats && fs->hard_age >= 0 && fs->hard_age != fs->duration_sec) {
- ds_put_format(string, "%shard_age=%s%d, ",
- colors.param, colors.end, fs->hard_age);
- }
-
- /* Print the match, followed by a space (but omit the space if the match
- * was an empty string). */
- size_t length = string->length;
- match_format(&fs->match, port_map, string, fs->priority);
- if (string->length != length) {
- ds_put_char(string, ' ');
+ enum ofperr error = ofputil_decode_flow_stats_request(&fsr, oh, NULL,
+ NULL);
+ if (!error) {
+ ofputil_flow_stats_request_format(string, &fsr, port_map, table_map);
}
-
- ds_put_format(string, "%sactions=%s", colors.actions, colors.end);
- struct ofpact_format_params fp = {
- .port_map = port_map,
- .table_map = table_map,
- .s = string,
- };
- ofpacts_format(fs->ofpacts, fs->ofpacts_len, &fp);
+ return error;
}
static enum ofperr
@@ -1823,7 +790,7 @@ ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh,
break;
}
ds_put_cstr(string, "\n ");
- ofp_print_flow_stats(string, &fs, port_map, table_map, true);
+ ofputil_flow_stats_format(string, &fs, port_map, table_map, true);
}
ofpbuf_uninit(&ofpacts);
@@ -1837,15 +804,10 @@ ofp_print_aggregate_stats_reply(struct ds *string, const struct ofp_header *oh)
enum ofperr error;
error = ofputil_decode_aggregate_stats_reply(&as, oh);
- if (error) {
- return error;
+ if (!error) {
+ ofputil_aggregate_stats_format(string, &as);
}
-
- ds_put_format(string, " packet_count=%"PRIu64, as.packet_count);
- ds_put_format(string, " byte_count=%"PRIu64, as.byte_count);
- ds_put_format(string, " flow_count=%"PRIu32, as.flow_count);
-
- return 0;
+ return error;
}
static void
@@ -2047,10 +1009,10 @@ ofp_print_table_stats_reply(struct ds *string, const struct ofp_header *oh,
}
ds_put_char(string, '\n');
- ofp_print_table_features(string,
- &features, i ? &prev_features : NULL,
- &stats, i ? &prev_stats : NULL,
- table_map);
+ ofputil_table_features_format(string,
+ &features, i ? &prev_features : NULL,
+ &stats, i ? &prev_stats : NULL,
+ table_map);
prev_features = features;
prev_stats = stats;
}
@@ -2153,7 +1115,7 @@ ofp_print_ofpst_port_desc_reply(struct ds *string,
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
ofpraw_pull_assert(&b);
ds_put_char(string, '\n');
- return ofp_print_phy_ports(string, oh->version, &b);
+ return ofputil_phy_ports_format(string, oh->version, &b);
}
static void
@@ -2480,23 +1442,6 @@ ofp_print_nxt_flow_monitor_cancel(struct ds *string,
return 0;
}
-static const char *
-nx_flow_monitor_flags_to_name(uint32_t bit)
-{
- enum nx_flow_monitor_flags fmf = bit;
-
- switch (fmf) {
- case NXFMF_INITIAL: return "initial";
- case NXFMF_ADD: return "add";
- case NXFMF_DELETE: return "delete";
- case NXFMF_MODIFY: return "modify";
- case NXFMF_ACTIONS: return "actions";
- case NXFMF_OWN: return "own";
- }
-
- return NULL;
-}
-
static enum ofperr
ofp_print_nxst_flow_monitor_request(struct ds *string,
const struct ofp_header *oh,
@@ -2513,23 +1458,8 @@ ofp_print_nxst_flow_monitor_request(struct ds *string,
return retval != EOF ? retval : 0;
}
- ds_put_format(string, "\n id=%"PRIu32" flags=", request.id);
- ofp_print_bit_names(string, request.flags,
- nx_flow_monitor_flags_to_name, ',');
-
- if (request.out_port != OFPP_NONE) {
- ds_put_cstr(string, " out_port=");
- ofputil_format_port(request.out_port, port_map, string);
- }
-
- if (request.table_id != 0xff) {
- ds_put_format(string, " table=");
- ofputil_format_table(request.table_id, table_map, string);
- }
-
- ds_put_char(string, ' ');
- match_format(&request.match, port_map, string, OFP_DEFAULT_PRIORITY);
- ds_chomp(string, ' ');
+ ofputil_flow_monitor_request_format(string, &request,
+ port_map, table_map);
}
}
@@ -2544,65 +1474,13 @@ ofp_print_nxst_flow_monitor_reply(struct ds *string,
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
for (;;) {
- char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE];
struct ofputil_flow_update update;
- int retval;
-
- retval = ofputil_decode_flow_update(&update, &b, &ofpacts);
+ int retval = ofputil_decode_flow_update(&update, &b, &ofpacts);
if (retval) {
ofpbuf_uninit(&ofpacts);
return retval != EOF ? retval : 0;
}
-
- ds_put_cstr(string, "\n event=");
- switch (update.event) {
- case NXFME_ADDED:
- ds_put_cstr(string, "ADDED");
- break;
-
- case NXFME_DELETED:
- ds_put_format(string, "DELETED reason=%s",
- ofp_flow_removed_reason_to_string(update.reason,
- reasonbuf,
- sizeof reasonbuf));
- break;
-
- case NXFME_MODIFIED:
- ds_put_cstr(string, "MODIFIED");
- break;
-
- case NXFME_ABBREV:
- ds_put_format(string, "ABBREV xid=0x%"PRIx32, ntohl(update.xid));
- continue;
- }
-
- ds_put_format(string, " table=");
- ofputil_format_table(update.table_id, table_map, string);
- if (update.idle_timeout != OFP_FLOW_PERMANENT) {
- ds_put_format(string, " idle_timeout=%"PRIu16,
- update.idle_timeout);
- }
- if (update.hard_timeout != OFP_FLOW_PERMANENT) {
- ds_put_format(string, " hard_timeout=%"PRIu16,
- update.hard_timeout);
- }
- ds_put_format(string, " cookie=%#"PRIx64, ntohll(update.cookie));
-
- ds_put_char(string, ' ');
- match_format(&update.match, port_map, string, OFP_DEFAULT_PRIORITY);
-
- if (update.ofpacts_len) {
- if (string->string[string->length - 1] != ' ') {
- ds_put_char(string, ' ');
- }
- ds_put_cstr(string, "actions=");
- struct ofpact_format_params fp = {
- .port_map = port_map,
- .table_map = table_map,
- .s = string,
- };
- ofpacts_format(update.ofpacts, update.ofpacts_len, &fp);
- }
+ ofputil_flow_update_format(string, &update, port_map, table_map);
}
}
@@ -2947,294 +1825,6 @@ ofp_print_group_mod(struct ds *s, const struct ofp_header *oh,
return 0;
}
-static void
-print_table_action_features(struct ds *s,
- const struct ofputil_table_action_features *taf)
-{
- if (taf->ofpacts) {
- ds_put_cstr(s, " actions: ");
- ofpact_bitmap_format(taf->ofpacts, s);
- ds_put_char(s, '\n');
- }
-
- if (!bitmap_is_all_zeros(taf->set_fields.bm, MFF_N_IDS)) {
- int i;
-
- ds_put_cstr(s, " supported on Set-Field:");
- BITMAP_FOR_EACH_1 (i, MFF_N_IDS, taf->set_fields.bm) {
- ds_put_format(s, " %s", mf_from_id(i)->name);
- }
- ds_put_char(s, '\n');
- }
-}
-
-static bool
-table_action_features_equal(const struct ofputil_table_action_features *a,
- const struct ofputil_table_action_features *b)
-{
- return (a->ofpacts == b->ofpacts
- && bitmap_equal(a->set_fields.bm, b->set_fields.bm, MFF_N_IDS));
-}
-
-static bool
-table_action_features_empty(const struct ofputil_table_action_features *taf)
-{
- return !taf->ofpacts && bitmap_is_all_zeros(taf->set_fields.bm, MFF_N_IDS);
-}
-
-static void
-print_table_instruction_features(
- struct ds *s,
- const struct ofputil_table_instruction_features *tif,
- const struct ofputil_table_instruction_features *prev_tif)
-{
- int start, end;
-
- if (!bitmap_is_all_zeros(tif->next, 255)) {
- ds_put_cstr(s, " next tables: ");
- for (start = bitmap_scan(tif->next, 1, 0, 255); start < 255;
- start = bitmap_scan(tif->next, 1, end, 255)) {
- end = bitmap_scan(tif->next, 0, start + 1, 255);
- if (end == start + 1) {
- ds_put_format(s, "%d,", start);
- } else {
- ds_put_format(s, "%d-%d,", start, end - 1);
- }
- }
- ds_chomp(s, ',');
- if (ds_last(s) == ' ') {
- ds_put_cstr(s, "none");
- }
- ds_put_char(s, '\n');
- }
-
- if (tif->instructions) {
- if (prev_tif && tif->instructions == prev_tif->instructions) {
- ds_put_cstr(s, " (same instructions)\n");
- } else {
- ds_put_cstr(s, " instructions: ");
- int i;
-
- for (i = 0; i < 32; i++) {
- if (tif->instructions & (1u << i)) {
- const char *name = ovs_instruction_name_from_type(i);
- if (name) {
- ds_put_cstr(s, name);
- } else {
- ds_put_format(s, "%d", i);
- }
- ds_put_char(s, ',');
- }
- }
- ds_chomp(s, ',');
- ds_put_char(s, '\n');
- }
- }
-
- if (prev_tif
- && table_action_features_equal(&tif->write, &prev_tif->write)
- && table_action_features_equal(&tif->apply, &prev_tif->apply)
- && !bitmap_is_all_zeros(tif->write.set_fields.bm, MFF_N_IDS)) {
- ds_put_cstr(s, " (same actions)\n");
- } else if (!table_action_features_equal(&tif->write, &tif->apply)) {
- ds_put_cstr(s, " Write-Actions features:\n");
- print_table_action_features(s, &tif->write);
- ds_put_cstr(s, " Apply-Actions features:\n");
- print_table_action_features(s, &tif->apply);
- } else if (tif->write.ofpacts
- || !bitmap_is_all_zeros(tif->write.set_fields.bm, MFF_N_IDS)) {
- ds_put_cstr(s, " Write-Actions and Apply-Actions features:\n");
- print_table_action_features(s, &tif->write);
- }
-}
-
-static bool
-table_instruction_features_equal(
- const struct ofputil_table_instruction_features *a,
- const struct ofputil_table_instruction_features *b)
-{
- return (bitmap_equal(a->next, b->next, 255)
- && a->instructions == b->instructions
- && table_action_features_equal(&a->write, &b->write)
- && table_action_features_equal(&a->apply, &b->apply));
-}
-
-static bool
-table_instruction_features_empty(
- const struct ofputil_table_instruction_features *tif)
-{
- return (bitmap_is_all_zeros(tif->next, 255)
- && !tif->instructions
- && table_action_features_empty(&tif->write)
- && table_action_features_empty(&tif->apply));
-}
-
-static bool
-table_features_equal(const struct ofputil_table_features *a,
- const struct ofputil_table_features *b)
-{
- return (a->metadata_match == b->metadata_match
- && a->metadata_write == b->metadata_write
- && a->miss_config == b->miss_config
- && a->supports_eviction == b->supports_eviction
- && a->supports_vacancy_events == b->supports_vacancy_events
- && a->max_entries == b->max_entries
- && table_instruction_features_equal(&a->nonmiss, &b->nonmiss)
- && table_instruction_features_equal(&a->miss, &b->miss)
- && bitmap_equal(a->match.bm, b->match.bm, MFF_N_IDS));
-}
-
-static bool
-table_features_empty(const struct ofputil_table_features *tf)
-{
- return (!tf->metadata_match
- && !tf->metadata_write
- && tf->miss_config == OFPUTIL_TABLE_MISS_DEFAULT
- && tf->supports_eviction < 0
- && tf->supports_vacancy_events < 0
- && !tf->max_entries
- && table_instruction_features_empty(&tf->nonmiss)
- && table_instruction_features_empty(&tf->miss)
- && bitmap_is_all_zeros(tf->match.bm, MFF_N_IDS));
-}
-
-static bool
-table_stats_equal(const struct ofputil_table_stats *a,
- const struct ofputil_table_stats *b)
-{
- return (a->active_count == b->active_count
- && a->lookup_count == b->lookup_count
- && a->matched_count == b->matched_count);
-}
-
-void
-ofp_print_table_features(struct ds *s,
- const struct ofputil_table_features *features,
- const struct ofputil_table_features *prev_features,
- const struct ofputil_table_stats *stats,
- const struct ofputil_table_stats *prev_stats,
- const struct ofputil_table_map *table_map)
-{
- int i;
-
- ds_put_format(s, " table ");
- ofputil_format_table(features->table_id, table_map, s);
- if (features->name[0]) {
- ds_put_format(s, " (\"%s\")", features->name);
- }
- ds_put_char(s, ':');
-
- bool same_stats = prev_stats && table_stats_equal(stats, prev_stats);
- bool same_features = prev_features && table_features_equal(features,
- prev_features);
- if ((!stats || same_stats) && same_features) {
- ds_put_cstr(s, " ditto");
- return;
- }
- ds_put_char(s, '\n');
- if (stats) {
- ds_put_format(s, " active=%"PRIu32", ", stats->active_count);
- ds_put_format(s, "lookup=%"PRIu64", ", stats->lookup_count);
- ds_put_format(s, "matched=%"PRIu64"\n", stats->matched_count);
- }
- if (same_features) {
- if (!table_features_empty(features)) {
- ds_put_cstr(s, " (same features)\n");
- }
- return;
- }
- if (features->metadata_match || features->metadata_write) {
- ds_put_format(s, " metadata: match=%#"PRIx64" write=%#"PRIx64"\n",
- ntohll(features->metadata_match),
- ntohll(features->metadata_write));
- }
-
- if (features->miss_config != OFPUTIL_TABLE_MISS_DEFAULT) {
- ds_put_format(s, " config=%s\n",
- ofputil_table_miss_to_string(features->miss_config));
- }
-
- if (features->supports_eviction >= 0) {
- ds_put_format(s, " eviction: %ssupported\n",
- features->supports_eviction ? "" : "not ");
-
- }
- if (features->supports_vacancy_events >= 0) {
- ds_put_format(s, " vacancy events: %ssupported\n",
- features->supports_vacancy_events ? "" : "not ");
-
- }
-
- if (features->max_entries) {
- ds_put_format(s, " max_entries=%"PRIu32"\n", features->max_entries);
- }
-
- const struct ofputil_table_instruction_features *prev_nonmiss
- = prev_features ? &prev_features->nonmiss : NULL;
- const struct ofputil_table_instruction_features *prev_miss
- = prev_features ? &prev_features->miss : NULL;
- if (prev_features
- && table_instruction_features_equal(&features->nonmiss, prev_nonmiss)
- && table_instruction_features_equal(&features->miss, prev_miss)) {
- if (!table_instruction_features_empty(&features->nonmiss)) {
- ds_put_cstr(s, " (same instructions)\n");
- }
- } else if (!table_instruction_features_equal(&features->nonmiss,
- &features->miss)) {
- ds_put_cstr(s, " instructions (other than table miss):\n");
- print_table_instruction_features(s, &features->nonmiss, prev_nonmiss);
- ds_put_cstr(s, " instructions (table miss):\n");
- print_table_instruction_features(s, &features->miss, prev_miss);
- } else if (!table_instruction_features_empty(&features->nonmiss)) {
- ds_put_cstr(s, " instructions (table miss and others):\n");
- print_table_instruction_features(s, &features->nonmiss, prev_nonmiss);
- }
-
- if (!bitmap_is_all_zeros(features->match.bm, MFF_N_IDS)) {
- if (prev_features
- && bitmap_equal(features->match.bm, prev_features->match.bm,
- MFF_N_IDS)) {
- ds_put_cstr(s, " (same matching)\n");
- } else {
- ds_put_cstr(s, " matching:\n");
- BITMAP_FOR_EACH_1 (i, MFF_N_IDS, features->match.bm) {
- const struct mf_field *f = mf_from_id(i);
- bool mask = bitmap_is_set(features->mask.bm, i);
- bool wildcard = bitmap_is_set(features->wildcard.bm, i);
-
- ds_put_format(s, " %s: %s\n",
- f->name,
- (mask ? "arbitrary mask"
- : wildcard ? "exact match or wildcard"
- : "must exact match"));
- }
- }
- }
-}
-
-static enum ofperr
-ofp_print_table_features_reply(struct ds *s, const struct ofp_header *oh,
- const struct ofputil_table_map *table_map)
-{
- struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
-
- struct ofputil_table_features prev;
- for (int i = 0; ; i++) {
- struct ofputil_table_features tf;
- int retval;
-
- retval = ofputil_decode_table_features(&b, &tf, true);
- if (retval) {
- return retval != EOF ? retval : 0;
- }
-
- ds_put_char(s, '\n');
- ofp_print_table_features(s, &tf, i ? &prev : NULL, NULL, NULL,
- table_map);
- prev = tf;
- }
-}
-
static enum ofperr
ofp_print_table_desc_reply(struct ds *s, const struct ofp_header *oh,
const struct ofputil_table_map *table_map)
@@ -3248,7 +1838,7 @@ ofp_print_table_desc_reply(struct ds *s, const struct ofp_header *oh,
if (retval) {
return retval != EOF ? retval : 0;
}
- ofp_print_table_desc(s, &td, table_map);
+ ofputil_table_desc_format(s, &td, table_map);
}
}
@@ -3619,7 +2209,8 @@ ofp_to_string__(const struct ofp_header *oh,
verbosity);
case OFPTYPE_FLOW_MOD:
- return ofp_print_flow_mod(string, oh, port_map, table_map, verbosity);
+ return ofputil_flow_mod_format(string, oh, port_map, table_map,
+ verbosity);
case OFPTYPE_PORT_MOD:
return ofp_print_port_mod(string, oh, port_map);
diff --git a/lib/ofp-switch.c b/lib/ofp-switch.c
index 3cd0fcae8..1fbfabed5 100644
--- a/lib/ofp-switch.c
+++ b/lib/ofp-switch.c
@@ -22,6 +22,7 @@
#include "openvswitch/ofp-errors.h"
#include "openvswitch/ofp-msgs.h"
#include "openvswitch/ofp-port.h"
+#include "openvswitch/ofp-print.h"
#include "util.h"
/* ofputil_switch_features */
@@ -229,6 +230,53 @@ ofputil_put_switch_features_port(const struct ofputil_phy_port *pp,
}
}
+static const char *
+ofputil_capabilities_to_name(uint32_t bit)
+{
+ enum ofputil_capabilities capabilities = bit;
+
+ switch (capabilities) {
+ case OFPUTIL_C_FLOW_STATS: return "FLOW_STATS";
+ case OFPUTIL_C_TABLE_STATS: return "TABLE_STATS";
+ case OFPUTIL_C_PORT_STATS: return "PORT_STATS";
+ case OFPUTIL_C_IP_REASM: return "IP_REASM";
+ case OFPUTIL_C_QUEUE_STATS: return "QUEUE_STATS";
+ case OFPUTIL_C_ARP_MATCH_IP: return "ARP_MATCH_IP";
+ case OFPUTIL_C_STP: return "STP";
+ case OFPUTIL_C_GROUP_STATS: return "GROUP_STATS";
+ case OFPUTIL_C_PORT_BLOCKED: return "PORT_BLOCKED";
+ case OFPUTIL_C_BUNDLES: return "BUNDLES";
+ case OFPUTIL_C_FLOW_MONITORING: return "FLOW_MONITORING";
+ }
+
+ return NULL;
+}
+
+void
+ofputil_switch_features_format(struct ds *s,
+ const struct ofputil_switch_features *features)
+{
+ ds_put_format(s, " dpid:%016"PRIx64"\n", features->datapath_id);
+
+ ds_put_format(s, "n_tables:%"PRIu8", n_buffers:%"PRIu32,
+ features->n_tables, features->n_buffers);
+ if (features->auxiliary_id) {
+ ds_put_format(s, ", auxiliary_id:%"PRIu8, features->auxiliary_id);
+ }
+ ds_put_char(s, '\n');
+
+ ds_put_cstr(s, "capabilities: ");
+ ofp_print_bit_names(s, features->capabilities,
+ ofputil_capabilities_to_name, ' ');
+ ds_put_char(s, '\n');
+
+ if (features->ofpacts) {
+ ds_put_cstr(s, "actions: ");
+ ofpact_bitmap_format(features->ofpacts, s);
+ ds_put_char(s, '\n');
+ }
+}
+
const char *
ofputil_frag_handling_to_string(enum ofputil_frag_handling frag)
{
@@ -334,3 +382,17 @@ ofputil_encode_set_config(const struct ofputil_switch_config *config,
struct ofpbuf *b = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, version, 0);
return ofputil_put_switch_config(config, b);
}
+
+void
+ofputil_switch_config_format(struct ds *s,
+ const struct ofputil_switch_config *config)
+{
+ ds_put_format(s, " frags=%s",
+ ofputil_frag_handling_to_string(config->frag));
+
+ if (config->invalid_ttl_to_controller > 0) {
+ ds_put_format(s, " invalid_ttl_to_controller");
+ }
+
+ ds_put_format(s, " miss_send_len=%"PRIu16"\n", config->miss_send_len);
+}
diff --git a/lib/ofp-table.c b/lib/ofp-table.c
index 558e4bcd9..595a4384d 100644
--- a/lib/ofp-table.c
+++ b/lib/ofp-table.c
@@ -22,6 +22,7 @@
#include "openvswitch/json.h"
#include "openvswitch/ofp-actions.h"
#include "openvswitch/ofp-msgs.h"
+#include "openvswitch/ofp-print.h"
#include "openvswitch/ofp-prop.h"
#include "openvswitch/ofpbuf.h"
#include "openvswitch/vlog.h"
@@ -40,6 +41,40 @@ static enum ofputil_table_vacancy ofputil_decode_table_vacancy(
static enum ofputil_table_eviction ofputil_decode_table_eviction(
ovs_be32 config, enum ofp_version);
+const char *
+ofputil_table_miss_to_string(enum ofputil_table_miss miss)
+{
+ switch (miss) {
+ case OFPUTIL_TABLE_MISS_DEFAULT: return "default";
+ case OFPUTIL_TABLE_MISS_CONTROLLER: return "controller";
+ case OFPUTIL_TABLE_MISS_CONTINUE: return "continue";
+ case OFPUTIL_TABLE_MISS_DROP: return "drop";
+ default: return "***error***";
+ }
+}
+
+const char *
+ofputil_table_eviction_to_string(enum ofputil_table_eviction eviction)
+{
+ switch (eviction) {
+ case OFPUTIL_TABLE_EVICTION_DEFAULT: return "default";
+ case OFPUTIL_TABLE_EVICTION_ON: return "on";
+ case OFPUTIL_TABLE_EVICTION_OFF: return "off";
+ default: return "***error***";
+ }
+}
+
+const char *
+ofputil_table_vacancy_to_string(enum ofputil_table_vacancy vacancy)
+{
+ switch (vacancy) {
+ case OFPUTIL_TABLE_VACANCY_DEFAULT: return "default";
+ case OFPUTIL_TABLE_VACANCY_ON: return "on";
+ case OFPUTIL_TABLE_VACANCY_OFF: return "off";
+ default: return "***error***";
+ }
+}
+
/* ofputil_table_map. */
void
@@ -708,6 +743,57 @@ ofputil_append_table_desc_reply(const struct ofputil_table_desc *td,
ofpmp_postappend(replies, start_otd);
}
+static const char *
+ofputil_eviction_flag_to_string(uint32_t bit)
+{
+ enum ofp14_table_mod_prop_eviction_flag eviction_flag = bit;
+
+ switch (eviction_flag) {
+ case OFPTMPEF14_OTHER: return "OTHER";
+ case OFPTMPEF14_IMPORTANCE: return "IMPORTANCE";
+ case OFPTMPEF14_LIFETIME: return "LIFETIME";
+ }
+
+ return NULL;
+}
+
+/* Appends to 'string' a description of the bitmap of OFPTMPEF14_* values in
+ * 'eviction_flags'. */
+static void
+ofputil_put_eviction_flags(struct ds *string, uint32_t eviction_flags)
+{
+ if (eviction_flags != UINT32_MAX) {
+ ofp_print_bit_names(string, eviction_flags,
+ ofputil_eviction_flag_to_string, '|');
+ } else {
+ ds_put_cstr(string, "(default)");
+ }
+}
+
+void
+ofputil_table_desc_format(struct ds *s, const struct ofputil_table_desc *td,
+ const struct ofputil_table_map *table_map)
+{
+ ds_put_format(s, "\n table ");
+ ofputil_format_table(td->table_id, table_map, s);
+ ds_put_cstr(s, ":\n");
+ ds_put_format(s, " eviction=%s eviction_flags=",
+ ofputil_table_eviction_to_string(td->eviction));
+ ofputil_put_eviction_flags(s, td->eviction_flags);
+ ds_put_char(s, '\n');
+ ds_put_format(s, " vacancy=%s",
+ ofputil_table_vacancy_to_string(td->vacancy));
+ if (td->vacancy == OFPUTIL_TABLE_VACANCY_ON) {
+ ds_put_format(s, " vacancy_down=%"PRIu8"%%",
+ td->table_vacancy.vacancy_down);
+ ds_put_format(s, " vacancy_up=%"PRIu8"%%",
+ td->table_vacancy.vacancy_up);
+ ds_put_format(s, " vacancy=%"PRIu8"%%",
+ td->table_vacancy.vacancy);
+ }
+ ds_put_char(s, '\n');
+}
+
/* This function parses Vacancy property, and decodes the
* ofp14_table_mod_prop_vacancy in ofputil_table_mod.
* Returns OFPERR_OFPBPC_BAD_VALUE error code when vacancy_down is
@@ -982,6 +1068,40 @@ ofputil_encode_table_mod(const struct ofputil_table_mod *tm,
return b;
}
+void
+ofputil_table_mod_format(struct ds *s, const struct ofputil_table_mod *tm,
+ const struct ofputil_table_map *table_map)
+{
+ if (tm->table_id == 0xff) {
+ ds_put_cstr(s, " table_id: ALL_TABLES");
+ } else {
+ ds_put_format(s, " table_id=");
+ ofputil_format_table(tm->table_id, table_map, s);
+ }
+
+ if (tm->miss != OFPUTIL_TABLE_MISS_DEFAULT) {
+ ds_put_format(s, ", flow_miss_config=%s",
+ ofputil_table_miss_to_string(tm->miss));
+ }
+ if (tm->eviction != OFPUTIL_TABLE_EVICTION_DEFAULT) {
+ ds_put_format(s, ", eviction=%s",
+ ofputil_table_eviction_to_string(tm->eviction));
+ }
+ if (tm->eviction_flags != UINT32_MAX) {
+ ds_put_cstr(s, "eviction_flags=");
+ ofputil_put_eviction_flags(s, tm->eviction_flags);
+ }
+ if (tm->vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) {
+ ds_put_format(s, ", vacancy=%s",
+ ofputil_table_vacancy_to_string(tm->vacancy));
+ if (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON) {
+ ds_put_format(s, " vacancy:%"PRIu8""
+ ",%"PRIu8"", tm->table_vacancy.vacancy_down,
+ tm->table_vacancy.vacancy_up);
+ }
+ }
+}
+
/* Convert 'setting' (as described for the "mod-table" command
* in ovs-ofctl man page) into 'tm->table_vacancy->vacancy_up' and
* 'tm->table_vacancy->vacancy_down' threshold values.
@@ -1107,6 +1227,272 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
return NULL;
}
+
+static void
+print_table_action_features(struct ds *s,
+ const struct ofputil_table_action_features *taf)
+{
+ if (taf->ofpacts) {
+ ds_put_cstr(s, " actions: ");
+ ofpact_bitmap_format(taf->ofpacts, s);
+ ds_put_char(s, '\n');
+ }
+
+ if (!bitmap_is_all_zeros(taf->set_fields.bm, MFF_N_IDS)) {
+ int i;
+
+ ds_put_cstr(s, " supported on Set-Field:");
+ BITMAP_FOR_EACH_1 (i, MFF_N_IDS, taf->set_fields.bm) {
+ ds_put_format(s, " %s", mf_from_id(i)->name);
+ }
+ ds_put_char(s, '\n');
+ }
+}
+
+static bool
+table_action_features_equal(const struct ofputil_table_action_features *a,
+ const struct ofputil_table_action_features *b)
+{
+ return (a->ofpacts == b->ofpacts
+ && bitmap_equal(a->set_fields.bm, b->set_fields.bm, MFF_N_IDS));
+}
+
+static bool
+table_action_features_empty(const struct ofputil_table_action_features *taf)
+{
+ return !taf->ofpacts && bitmap_is_all_zeros(taf->set_fields.bm, MFF_N_IDS);
+}
+
+static void
+print_table_instruction_features(
+ struct ds *s,
+ const struct ofputil_table_instruction_features *tif,
+ const struct ofputil_table_instruction_features *prev_tif)
+{
+ int start, end;
+
+ if (!bitmap_is_all_zeros(tif->next, 255)) {
+ ds_put_cstr(s, " next tables: ");
+ for (start = bitmap_scan(tif->next, 1, 0, 255); start < 255;
+ start = bitmap_scan(tif->next, 1, end, 255)) {
+ end = bitmap_scan(tif->next, 0, start + 1, 255);
+ if (end == start + 1) {
+ ds_put_format(s, "%d,", start);
+ } else {
+ ds_put_format(s, "%d-%d,", start, end - 1);
+ }
+ }
+ ds_chomp(s, ',');
+ if (ds_last(s) == ' ') {
+ ds_put_cstr(s, "none");
+ }
+ ds_put_char(s, '\n');
+ }
+
+ if (tif->instructions) {
+ if (prev_tif && tif->instructions == prev_tif->instructions) {
+ ds_put_cstr(s, " (same instructions)\n");
+ } else {
+ ds_put_cstr(s, " instructions: ");
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ if (tif->instructions & (1u << i)) {
+ const char *name = ovs_instruction_name_from_type(i);
+ if (name) {
+ ds_put_cstr(s, name);
+ } else {
+ ds_put_format(s, "%d", i);
+ }
+ ds_put_char(s, ',');
+ }
+ }
+ ds_chomp(s, ',');
+ ds_put_char(s, '\n');
+ }
+ }
+
+ if (prev_tif
+ && table_action_features_equal(&tif->write, &prev_tif->write)
+ && table_action_features_equal(&tif->apply, &prev_tif->apply)
+ && !bitmap_is_all_zeros(tif->write.set_fields.bm, MFF_N_IDS)) {
+ ds_put_cstr(s, " (same actions)\n");
+ } else if (!table_action_features_equal(&tif->write, &tif->apply)) {
+ ds_put_cstr(s, " Write-Actions features:\n");
+ print_table_action_features(s, &tif->write);
+ ds_put_cstr(s, " Apply-Actions features:\n");
+ print_table_action_features(s, &tif->apply);
+ } else if (tif->write.ofpacts
+ || !bitmap_is_all_zeros(tif->write.set_fields.bm, MFF_N_IDS)) {
+ ds_put_cstr(s, " Write-Actions and Apply-Actions features:\n");
+ print_table_action_features(s, &tif->write);
+ }
+}
+
+static bool
+table_instruction_features_equal(
+ const struct ofputil_table_instruction_features *a,
+ const struct ofputil_table_instruction_features *b)
+{
+ return (bitmap_equal(a->next, b->next, 255)
+ && a->instructions == b->instructions
+ && table_action_features_equal(&a->write, &b->write)
+ && table_action_features_equal(&a->apply, &b->apply));
+}
+
+static bool
+table_instruction_features_empty(
+ const struct ofputil_table_instruction_features *tif)
+{
+ return (bitmap_is_all_zeros(tif->next, 255)
+ && !tif->instructions
+ && table_action_features_empty(&tif->write)
+ && table_action_features_empty(&tif->apply));
+}
+
+static bool
+table_features_equal(const struct ofputil_table_features *a,
+ const struct ofputil_table_features *b)
+{
+ return (a->metadata_match == b->metadata_match
+ && a->metadata_write == b->metadata_write
+ && a->miss_config == b->miss_config
+ && a->supports_eviction == b->supports_eviction
+ && a->supports_vacancy_events == b->supports_vacancy_events
+ && a->max_entries == b->max_entries
+ && table_instruction_features_equal(&a->nonmiss, &b->nonmiss)
+ && table_instruction_features_equal(&a->miss, &b->miss)
+ && bitmap_equal(a->match.bm, b->match.bm, MFF_N_IDS));
+}
+
+static bool
+table_features_empty(const struct ofputil_table_features *tf)
+{
+ return (!tf->metadata_match
+ && !tf->metadata_write
+ && tf->miss_config == OFPUTIL_TABLE_MISS_DEFAULT
+ && tf->supports_eviction < 0
+ && tf->supports_vacancy_events < 0
+ && !tf->max_entries
+ && table_instruction_features_empty(&tf->nonmiss)
+ && table_instruction_features_empty(&tf->miss)
+ && bitmap_is_all_zeros(tf->match.bm, MFF_N_IDS));
+}
+
+static bool
+table_stats_equal(const struct ofputil_table_stats *a,
+ const struct ofputil_table_stats *b)
+{
+ return (a->active_count == b->active_count
+ && a->lookup_count == b->lookup_count
+ && a->matched_count == b->matched_count);
+}
+
+void
+ofputil_table_features_format(
+ struct ds *s,
+ const struct ofputil_table_features *features,
+ const struct ofputil_table_features *prev_features,
+ const struct ofputil_table_stats *stats,
+ const struct ofputil_table_stats *prev_stats,
+ const struct ofputil_table_map *table_map)
+{
+ int i;
+
+ ds_put_format(s, " table ");
+ ofputil_format_table(features->table_id, table_map, s);
+ if (features->name[0]) {
+ ds_put_format(s, " (\"%s\")", features->name);
+ }
+ ds_put_char(s, ':');
+
+ bool same_stats = prev_stats && table_stats_equal(stats, prev_stats);
+ bool same_features = prev_features && table_features_equal(features,
+ prev_features);
+ if ((!stats || same_stats) && same_features) {
+ ds_put_cstr(s, " ditto");
+ return;
+ }
+ ds_put_char(s, '\n');
+ if (stats) {
+ ds_put_format(s, " active=%"PRIu32", ", stats->active_count);
+ ds_put_format(s, "lookup=%"PRIu64", ", stats->lookup_count);
+ ds_put_format(s, "matched=%"PRIu64"\n", stats->matched_count);
+ }
+ if (same_features) {
+ if (!table_features_empty(features)) {
+ ds_put_cstr(s, " (same features)\n");
+ }
+ return;
+ }
+ if (features->metadata_match || features->metadata_write) {
+ ds_put_format(s, " metadata: match=%#"PRIx64" write=%#"PRIx64"\n",
+ ntohll(features->metadata_match),
+ ntohll(features->metadata_write));
+ }
+
+ if (features->miss_config != OFPUTIL_TABLE_MISS_DEFAULT) {
+ ds_put_format(s, " config=%s\n",
+ ofputil_table_miss_to_string(features->miss_config));
+ }
+
+ if (features->supports_eviction >= 0) {
+ ds_put_format(s, " eviction: %ssupported\n",
+ features->supports_eviction ? "" : "not ");
+
+ }
+ if (features->supports_vacancy_events >= 0) {
+ ds_put_format(s, " vacancy events: %ssupported\n",
+ features->supports_vacancy_events ? "" : "not ");
+
+ }
+
+ if (features->max_entries) {
+ ds_put_format(s, " max_entries=%"PRIu32"\n", features->max_entries);
+ }
+
+ const struct ofputil_table_instruction_features *prev_nonmiss
+ = prev_features ? &prev_features->nonmiss : NULL;
+ const struct ofputil_table_instruction_features *prev_miss
+ = prev_features ? &prev_features->miss : NULL;
+ if (prev_features
+ && table_instruction_features_equal(&features->nonmiss, prev_nonmiss)
+ && table_instruction_features_equal(&features->miss, prev_miss)) {
+ if (!table_instruction_features_empty(&features->nonmiss)) {
+ ds_put_cstr(s, " (same instructions)\n");
+ }
+ } else if (!table_instruction_features_equal(&features->nonmiss,
+ &features->miss)) {
+ ds_put_cstr(s, " instructions (other than table miss):\n");
+ print_table_instruction_features(s, &features->nonmiss, prev_nonmiss);
+ ds_put_cstr(s, " instructions (table miss):\n");
+ print_table_instruction_features(s, &features->miss, prev_miss);
+ } else if (!table_instruction_features_empty(&features->nonmiss)) {
+ ds_put_cstr(s, " instructions (table miss and others):\n");
+ print_table_instruction_features(s, &features->nonmiss, prev_nonmiss);
+ }
+
+ if (!bitmap_is_all_zeros(features->match.bm, MFF_N_IDS)) {
+ if (prev_features
+ && bitmap_equal(features->match.bm, prev_features->match.bm,
+ MFF_N_IDS)) {
+ ds_put_cstr(s, " (same matching)\n");
+ } else {
+ ds_put_cstr(s, " matching:\n");
+ BITMAP_FOR_EACH_1 (i, MFF_N_IDS, features->match.bm) {
+ const struct mf_field *f = mf_from_id(i);
+ bool mask = bitmap_is_set(features->mask.bm, i);
+ bool wildcard = bitmap_is_set(features->wildcard.bm, i);
+
+ ds_put_format(s, " %s: %s\n",
+ f->name,
+ (mask ? "arbitrary mask"
+ : wildcard ? "exact match or wildcard"
+ : "must exact match"));
+ }
+ }
+ }
+}
/* Table stats. */
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index c78c856ea..ebbee077c 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -173,6 +173,23 @@ ofputil_encode_hello(uint32_t allowed_versions)
return msg;
}
+
+void
+ofputil_hello_format(struct ds *string, const struct ofp_header *oh)
+{
+ uint32_t allowed_versions;
+ bool ok;
+
+ ok = ofputil_decode_hello(oh, &allowed_versions);
+
+ ds_put_cstr(string, "\n version bitmap: ");
+ ofputil_format_version_bitmap(string, allowed_versions);
+
+ if (!ok) {
+ ds_put_cstr(string, "\n unknown data in hello:\n");
+ ds_put_hex_dump(string, oh, ntohs(oh->length), 0, true);
+ }
+}
/* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */
struct ofpbuf *
diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c
index c2fd18338..32ce71def 100644
--- a/ovn/utilities/ovn-sbctl.c
+++ b/ovn/utilities/ovn-sbctl.c
@@ -798,7 +798,7 @@ sbctl_dump_openflow(struct vconn *vconn, const struct uuid *uuid, bool stats)
ds_clear(&s);
if (stats) {
- ofp_print_flow_stats(&s, fs, NULL, NULL, true);
+ ofputil_flow_stats_format(&s, fs, NULL, NULL, true);
} else {
ds_put_format(&s, "%stable=%s%"PRIu8" ",
colors.special, colors.end, fs->table_id);
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index 00e885a14..a3925734d 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -1967,7 +1967,7 @@ trace_openflow(const struct ovntrace_flow *f, struct ovs_list *super)
struct ds s = DS_EMPTY_INITIALIZER;
for (size_t i = 0; i < n_fses; i++) {
ds_clear(&s);
- ofp_print_flow_stats(&s, &fses[i], NULL, NULL, true);
+ ofputil_flow_stats_format(&s, &fses[i], NULL, NULL, true);
ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
"%s", ds_cstr(&s));
}
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 77aa49649..b5e2b4093 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -926,9 +926,9 @@ ofctl_dump_table_features(struct ovs_cmdl_context *ctx)
}
struct ds s = DS_EMPTY_INITIALIZER;
- ofp_print_table_features(&s, &tf, n ? &prev : NULL,
- NULL, NULL,
- tables_to_show(ctx->argv[1]));
+ ofputil_table_features_format(
+ &s, &tf, n ? &prev : NULL, NULL, NULL,
+ tables_to_show(ctx->argv[1]));
puts(ds_cstr(&s));
ds_destroy(&s);
@@ -1571,8 +1571,10 @@ ofctl_dump_flows(struct ovs_cmdl_context *ctx)
struct ds s = DS_EMPTY_INITIALIZER;
for (size_t i = 0; i < n_fses; i++) {
ds_clear(&s);
- ofp_print_flow_stats(&s, &fses[i], ports_to_show(ctx->argv[1]),
- tables_to_show(ctx->argv[1]), show_stats);
+ ofputil_flow_stats_format(&s, &fses[i],
+ ports_to_show(ctx->argv[1]),
+ tables_to_show(ctx->argv[1]),
+ show_stats);
printf(" %s\n", ds_cstr(&s));
}
ds_destroy(&s);