diff options
-rw-r--r-- | include/openvswitch/netdev.h | 2 | ||||
-rw-r--r-- | include/openvswitch/ofp-errors.h | 6 | ||||
-rw-r--r-- | include/openvswitch/ofp-flow.h | 17 | ||||
-rw-r--r-- | include/openvswitch/ofp-match.h | 5 | ||||
-rw-r--r-- | include/openvswitch/ofp-monitor.h | 17 | ||||
-rw-r--r-- | include/openvswitch/ofp-packet.h | 11 | ||||
-rw-r--r-- | include/openvswitch/ofp-port.h | 13 | ||||
-rw-r--r-- | include/openvswitch/ofp-print.h | 22 | ||||
-rw-r--r-- | include/openvswitch/ofp-switch.h | 5 | ||||
-rw-r--r-- | include/openvswitch/ofp-table.h | 51 | ||||
-rw-r--r-- | include/openvswitch/ofp-util.h | 1 | ||||
-rw-r--r-- | lib/netdev.c | 35 | ||||
-rw-r--r-- | lib/ofp-errors.c | 19 | ||||
-rw-r--r-- | lib/ofp-flow.c | 257 | ||||
-rw-r--r-- | lib/ofp-match.c | 154 | ||||
-rw-r--r-- | lib/ofp-monitor.c | 162 | ||||
-rw-r--r-- | lib/ofp-packet.c | 161 | ||||
-rw-r--r-- | lib/ofp-port.c | 218 | ||||
-rw-r--r-- | lib/ofp-print.c | 1561 | ||||
-rw-r--r-- | lib/ofp-switch.c | 62 | ||||
-rw-r--r-- | lib/ofp-table.c | 386 | ||||
-rw-r--r-- | lib/ofp-util.c | 17 | ||||
-rw-r--r-- | ovn/utilities/ovn-sbctl.c | 2 | ||||
-rw-r--r-- | ovn/utilities/ovn-trace.c | 2 | ||||
-rw-r--r-- | utilities/ovs-ofctl.c | 12 |
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); |