OpenFlow 1.0 is limited to displaying 1364 ports in the Features Reply message, and there is no other way to get consolidated port information. OpenFlow 1.3 adds a new port description multipart message (OFPMP_PORT_DESC) that is not limited by size. This commit adds support through the OpenFlow 1.0 stats mechanism, since they have complimentary enum values.
Bug #11040 Signed-off-by: Justin Pettit <[email protected]> --- include/openflow/openflow-common.h | 7 +++ lib/learning-switch.c | 4 +- lib/ofp-print.c | 39 ++++++++++++++- lib/ofp-util.c | 96 ++++++++++++++++++++---------------- lib/ofp-util.h | 15 +++++- ofproto/ofproto.c | 23 +++++++++ tests/ofproto.at | 15 ++++++ utilities/ovs-ofctl.c | 10 +++- 8 files changed, 160 insertions(+), 49 deletions(-) diff --git a/include/openflow/openflow-common.h b/include/openflow/openflow-common.h index 49ae85f..0234c31 100644 --- a/include/openflow/openflow-common.h +++ b/include/openflow/openflow-common.h @@ -273,6 +273,13 @@ enum ofp_stats_types { * The OF1.0 reply body is an array of struct ofp_queue_stats. */ OFPST_QUEUE, + /* Port description. (OFPMP_PORT_DESC) + * This was introduced as part of OF1.3, but is useful for bridges + * with many ports, so we support it with OF1.0, too. + * The OF1.0 request is struct ofp_stats_msg. + * The OF1.0 reply body is an array of struct ofp10_phy_port. */ + OFPST_PORT_DESC = 13, + /* Vendor extension. * The OF1.0 request and reply begin with struct ofp_vendor_stats. */ OFPST_VENDOR = 0xffff diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 74f51fe..4e7ceda 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -278,12 +278,14 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, case OFPUTIL_OFPST_TABLE_REQUEST: case OFPUTIL_OFPST_PORT_REQUEST: case OFPUTIL_OFPST_QUEUE_REQUEST: + case OFPUTIL_OFPST_PORT_DESC_REQUEST: case OFPUTIL_OFPST_DESC_REPLY: case OFPUTIL_OFPST_FLOW_REPLY: case OFPUTIL_OFPST_QUEUE_REPLY: case OFPUTIL_OFPST_PORT_REPLY: case OFPUTIL_OFPST_TABLE_REPLY: case OFPUTIL_OFPST_AGGREGATE_REPLY: + case OFPUTIL_OFPST_PORT_DESC_REPLY: case OFPUTIL_NXT_ROLE_REQUEST: case OFPUTIL_NXT_ROLE_REPLY: case OFPUTIL_NXT_FLOW_MOD_TABLE_ID: @@ -363,7 +365,7 @@ process_switch_features(struct lswitch *sw, struct ofp_switch_features *osf) sw->datapath_id = features.datapath_id; - while (!ofputil_pull_switch_features_port(&b, &port)) { + while (!ofputil_pull_phy_port(osf->header.version, &b, &port)) { struct lswitch_port *lp = shash_find_data(&sw->queue_names, port.name); if (lp && hmap_node_is_null(&lp->hmap_node)) { lp->port_no = port.port_no; diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 5b84098..9539568 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -730,11 +730,11 @@ ofp_print_switch_features(struct ds *string, ofputil_action_bitmap_to_name); ds_put_char(string, '\n'); - n_ports = ofputil_count_phy_ports(osf); + n_ports = ofputil_count_phy_ports(osf->header.version, &b); ports = xmalloc(n_ports * sizeof *ports); for (i = 0; i < n_ports; i++) { - error = ofputil_pull_switch_features_port(&b, &ports[i]); + error = ofputil_pull_phy_port(osf->header.version, &b, &ports[i]); if (error) { ofp_print_error(string, error); return; @@ -1393,6 +1393,35 @@ ofp_print_ofpst_queue_reply(struct ds *string, const struct ofp_header *oh, } static void +ofp_print_ofpst_port_desc_reply(struct ds *string, + const struct ofp_header *oh, int verbosity) +{ + size_t n_ports; + enum ofperr error; + struct ofputil_phy_port pp; + struct ofpbuf b; + int i; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + ofpbuf_pull(&b, sizeof(struct ofp_stats_msg)); + + n_ports = ofputil_count_phy_ports(oh->version, &b); + ds_put_format(string, " %zu ports\n", n_ports); + if (verbosity < 1) { + return; + } + + for (i = 0; i < n_ports; i++) { + error = ofputil_pull_phy_port(oh->version, &b, &pp); + if (error) { + ofp_print_error(string, error); + return; + } + ofp_print_phy_port(string, &pp); + } +} + +static void ofp_print_stats_request(struct ds *string, const struct ofp_header *oh) { const struct ofp_stats_msg *srq = (const struct ofp_stats_msg *) oh; @@ -1656,6 +1685,7 @@ ofp_to_string__(const struct ofp_header *oh, break; case OFPUTIL_OFPST_DESC_REQUEST: + case OFPUTIL_OFPST_PORT_DESC_REQUEST: ofp_print_stats_request(string, oh); break; @@ -1712,6 +1742,11 @@ ofp_to_string__(const struct ofp_header *oh, ofp_print_ofpst_aggregate_reply(string, msg); break; + case OFPUTIL_OFPST_PORT_DESC_REPLY: + ofp_print_stats_reply(string, oh); + ofp_print_ofpst_port_desc_reply(string, oh, verbosity); + break; + case OFPUTIL_NXT_ROLE_REQUEST: case OFPUTIL_NXT_ROLE_REPLY: ofp_print_nxt_role_message(string, msg); diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 43776d5..f4ca110 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -587,6 +587,10 @@ ofputil_decode_ofpst_request(const struct ofp_header *oh, size_t length, OFPST_QUEUE, "OFPST_QUEUE request", sizeof(struct ofp_queue_stats_request), 0 }, + { OFPUTIL_OFPST_PORT_DESC_REQUEST, OFP10_VERSION, + OFPST_PORT_DESC, "OFPST_PORT_DESC request", + sizeof(struct ofp_stats_msg), 0 }, + { 0, 0, OFPST_VENDOR, "OFPST_VENDOR request", sizeof(struct ofp_vendor_stats_msg), 1 }, @@ -644,6 +648,10 @@ ofputil_decode_ofpst_reply(const struct ofp_header *oh, size_t length, OFPST_QUEUE, "OFPST_QUEUE reply", sizeof(struct ofp_stats_msg), sizeof(struct ofp_queue_stats) }, + { OFPUTIL_OFPST_PORT_DESC_REPLY, OFP10_VERSION, + OFPST_PORT_DESC, "OFPST_PORT_DESC reply", + sizeof(struct ofp_stats_msg), sizeof(struct ofp10_phy_port) }, + { 0, 0, OFPST_VENDOR, "OFPST_VENDOR reply", sizeof(struct ofp_vendor_stats_msg), 1 }, @@ -2363,19 +2371,6 @@ ofputil_decode_ofp11_port(struct ofputil_phy_port *pp, return 0; } -static int -ofputil_pull_phy_port(uint8_t ofp_version, struct ofpbuf *b, - struct ofputil_phy_port *pp) -{ - if (ofp_version == OFP10_VERSION) { - const struct ofp10_phy_port *opp = ofpbuf_try_pull(b, sizeof *opp); - return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF; - } else { - const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op); - return op ? ofputil_decode_ofp11_port(pp, op) : EOF; - } -} - static void ofputil_encode_ofp10_phy_port(const struct ofputil_phy_port *pp, struct ofp10_phy_port *opp) @@ -2435,6 +2430,24 @@ ofputil_put_phy_port(uint8_t ofp_version, const struct ofputil_phy_port *pp, } } } + +void +ofputil_append_port_desc_stats_reply(uint8_t ofp_version, + const struct ofputil_phy_port *pp, + struct list *replies) +{ + if (ofp_version == OFP10_VERSION) { + struct ofp10_phy_port *opp; + + opp = ofputil_append_stats_reply(sizeof *opp, replies); + ofputil_encode_ofp10_phy_port(pp, opp); + } else { + struct ofp11_port *op; + + op = ofputil_append_stats_reply(sizeof *op, replies); + ofputil_encode_ofp11_port(pp, op); + } +} /* ofputil_switch_features */ @@ -2515,7 +2528,7 @@ decode_action_bits(ovs_be32 of_actions, /* Decodes an OpenFlow 1.0 or 1.1 "switch_features" structure 'osf' into an * abstract representation in '*features'. Initializes '*b' to iterate over * the OpenFlow port structures following 'osf' with later calls to - * ofputil_pull_switch_features_port(). Returns 0 if successful, otherwise an + * ofputil_pull_phy_port(). Returns 0 if successful, otherwise an * OFPERR_* value. */ enum ofperr ofputil_decode_switch_features(const struct ofp_switch_features *osf, @@ -2524,7 +2537,6 @@ ofputil_decode_switch_features(const struct ofp_switch_features *osf, { ofpbuf_use_const(b, osf, ntohs(osf->header.length)); ofpbuf_pull(b, sizeof *osf); - b->l2 = (struct ofputil_switch_features *) osf; features->datapath_id = ntohll(osf->datapath_id); features->n_buffers = ntohl(osf->n_buffers); @@ -2556,33 +2568,6 @@ ofputil_decode_switch_features(const struct ofp_switch_features *osf, return 0; } -/* Given a buffer 'b' that was initialized by a previous successful call to - * ofputil_decode_switch_features(), tries to decode an OpenFlow port structure - * following the main switch features information. If successful, initializes - * '*pp' with an abstract representation of the port and returns 0. If no - * ports remained to be decoded, returns EOF. On an error, returns a positive - * OFPERR_* value. */ -int -ofputil_pull_switch_features_port(struct ofpbuf *b, - struct ofputil_phy_port *pp) -{ - const struct ofp_switch_features *osf = b->l2; - return ofputil_pull_phy_port(osf->header.version, b, pp); -} - -/* Returns the number of OpenFlow port structures that follow the main switch - * features information in '*osf'. The return value is only guaranteed to be - * accurate if '*osf' is well-formed, that is, if - * ofputil_decode_switch_features() can process '*osf' successfully. */ -size_t -ofputil_count_phy_ports(const struct ofp_switch_features *osf) -{ - size_t ports_len = ntohs(osf->header.length) - sizeof *osf; - return (osf->header.version == OFP10_VERSION - ? ports_len / sizeof(struct ofp10_phy_port) - : ports_len / sizeof(struct ofp11_port)); -} - static ovs_be32 encode_action_bits(enum ofputil_action_bitmap ofputil_actions, const struct ofputil_action_bit_translation *x) @@ -3362,6 +3347,33 @@ ofputil_format_port(uint16_t port, struct ds *s) ds_put_cstr(s, name); } +/* Given a buffer 'b' that contains an array of OpenFlow ports of type + * 'ofp_version', tries to pull the first element from the array. If + * successful, initializes '*pp' with an abstract representation of the + * port and returns 0. If no ports remain to be decoded, returns EOF. + * On an error, returns a positive OFPERR_* value. */ +int +ofputil_pull_phy_port(uint8_t ofp_version, struct ofpbuf *b, + struct ofputil_phy_port *pp) +{ + if (ofp_version == OFP10_VERSION) { + const struct ofp10_phy_port *opp = ofpbuf_try_pull(b, sizeof *opp); + return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF; + } else { + const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op); + return op ? ofputil_decode_ofp11_port(pp, op) : EOF; + } +} + +/* Given a buffer 'b' that contains an array of OpenFlow ports of type + * 'ofp_version', returns the number of elements. */ +size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *b) +{ + return (ofp_version == OFP10_VERSION + ? b->size / sizeof(struct ofp10_phy_port) + : b->size / sizeof(struct ofp11_port)); +} + static enum ofperr check_resubmit_table(const struct nx_action_resubmit *nar) { diff --git a/lib/ofp-util.h b/lib/ofp-util.h index bf2ae07..43bbf31 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -62,6 +62,7 @@ enum ofputil_msg_code { OFPUTIL_OFPST_TABLE_REQUEST, OFPUTIL_OFPST_PORT_REQUEST, OFPUTIL_OFPST_QUEUE_REQUEST, + OFPUTIL_OFPST_PORT_DESC_REQUEST, /* OFPST_* stat replies. */ OFPUTIL_OFPST_DESC_REPLY, @@ -70,6 +71,7 @@ enum ofputil_msg_code { OFPUTIL_OFPST_PORT_REPLY, OFPUTIL_OFPST_TABLE_REPLY, OFPUTIL_OFPST_AGGREGATE_REPLY, + OFPUTIL_OFPST_PORT_DESC_REPLY, /* NXT_* messages. */ OFPUTIL_NXT_ROLE_REQUEST, @@ -432,9 +434,6 @@ struct ofputil_switch_features { enum ofperr ofputil_decode_switch_features(const struct ofp_switch_features *, struct ofputil_switch_features *, struct ofpbuf *); -int ofputil_pull_switch_features_port(struct ofpbuf *, - struct ofputil_phy_port *); -size_t ofputil_count_phy_ports(const struct ofp_switch_features *); struct ofpbuf *ofputil_encode_switch_features( const struct ofputil_switch_features *, enum ofputil_protocol, @@ -442,6 +441,11 @@ struct ofpbuf *ofputil_encode_switch_features( void ofputil_put_switch_features_port(const struct ofputil_phy_port *, struct ofpbuf *); +/* phy_port helper functions. */ +int ofputil_pull_phy_port(uint8_t ofp_version, struct ofpbuf *, + struct ofputil_phy_port *); +size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *); + /* Abstract ofp_port_status. */ struct ofputil_port_status { enum ofp_port_reason reason; @@ -497,6 +501,10 @@ void ofputil_start_stats_reply(const struct ofp_stats_msg *request, struct ofpbuf *ofputil_reserve_stats_reply(size_t len, struct list *); void *ofputil_append_stats_reply(size_t len, struct list *); +void ofputil_append_port_desc_stats_reply(uint8_t ofp_version, + const struct ofputil_phy_port *pp, + struct list *replies); + const void *ofputil_stats_body(const struct ofp_header *); size_t ofputil_stats_body_len(const struct ofp_header *); @@ -521,6 +529,7 @@ struct ofpbuf *ofputil_encode_barrier_request(void); const char *ofputil_frag_handling_to_string(enum ofp_config_flags); bool ofputil_frag_handling_from_string(const char *, enum ofp_config_flags *); + /* Actions. */ diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 4b2cbc9..806e56b 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -2187,6 +2187,25 @@ handle_port_stats_request(struct ofconn *ofconn, return 0; } +static enum ofperr +handle_port_desc_stats_request(struct ofconn *ofconn, + const struct ofp_stats_msg *osm) +{ + struct ofproto *p = ofconn_get_ofproto(ofconn); + struct ofport *port; + struct list replies; + + ofputil_start_stats_reply(osm, &replies); + + HMAP_FOR_EACH (port, hmap_node, &p->ports) { + ofputil_append_port_desc_stats_reply(ofconn_get_protocol(ofconn), + &port->pp, &replies); + } + + ofconn_send_replies(ofconn, &replies); + return 0; +} + static void calc_flow_duration__(long long int start, long long int now, uint32_t *sec, uint32_t *nsec) @@ -3309,6 +3328,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) case OFPUTIL_OFPST_QUEUE_REQUEST: return handle_queue_stats_request(ofconn, msg->data); + case OFPUTIL_OFPST_PORT_DESC_REQUEST: + return handle_port_desc_stats_request(ofconn, msg->data); + case OFPUTIL_MSG_INVALID: case OFPUTIL_OFPT_HELLO: case OFPUTIL_OFPT_ERROR: @@ -3326,6 +3348,7 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) case OFPUTIL_OFPST_PORT_REPLY: case OFPUTIL_OFPST_TABLE_REPLY: case OFPUTIL_OFPST_AGGREGATE_REPLY: + case OFPUTIL_OFPST_PORT_DESC_REPLY: case OFPUTIL_NXT_ROLE_REPLY: case OFPUTIL_NXT_FLOW_REMOVED: case OFPUTIL_NXT_PACKET_IN: diff --git a/tests/ofproto.at b/tests/ofproto.at index 82f2f9c..df38c69 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -38,6 +38,21 @@ AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. +AT_SETUP([ofproto - port-desc stats]) +OVS_VSWITCHD_START +AT_CHECK([ovs-ofctl -vANY:ANY:WARN dump-ports-desc br0], [0], [stdout]) +AT_CHECK([STRIP_XIDS stdout], [0], [dnl +OFPST_PORT_DESC reply: 1 ports + LOCAL(br0): addr:aa:55:aa:55:00:00 + config: PORT_DOWN + state: LINK_DOWN + speed: 100 Mbps now, 100 Mbps max +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +dnl This is really bare-bones. +dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - queue stats]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -vANY:ANY:WARN queue-stats br0], [0], [stdout]) diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 86c0a85..4a37067 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -210,6 +210,7 @@ usage(void) " get-frags SWITCH print fragment handling behavior\n" " set-frags SWITCH FRAG_MODE set fragment handling behavior\n" " dump-ports SWITCH [PORT] print port statistics\n" + " dump-ports-desc SWITCH print port descriptions\n" " dump-flows SWITCH print all flow entries\n" " dump-flows SWITCH FLOW print matching FLOWs\n" " dump-aggregate SWITCH print aggregate flow statistics\n" @@ -547,7 +548,7 @@ fetch_ofputil_phy_port(const char *vconn_name, const char *port_name, vconn_name, ofperr_to_string(error)); } - while (!ofputil_pull_switch_features_port(&b, pp)) { + while (!ofputil_pull_phy_port(osf->header.version, &b, pp)) { if (port_no != UINT_MAX ? port_no == pp->port_no : !strcmp(pp->name, port_name)) { @@ -1085,6 +1086,12 @@ do_dump_ports(int argc, char *argv[]) } static void +do_dump_ports_desc(int argc OVS_UNUSED, char *argv[]) +{ + dump_trivial_stats_transaction(argv[1], OFPST_PORT_DESC); +} + +static void do_probe(int argc OVS_UNUSED, char *argv[]) { struct ofpbuf *request; @@ -1896,6 +1903,7 @@ static const struct command all_commands[] = { { "diff-flows", 2, 2, do_diff_flows }, { "packet-out", 4, INT_MAX, do_packet_out }, { "dump-ports", 1, 2, do_dump_ports }, + { "dump-ports-desc", 1, 1, do_dump_ports_desc }, { "mod-port", 3, 3, do_mod_port }, { "get-frags", 1, 1, do_get_frags }, { "set-frags", 2, 2, do_set_frags }, -- 1.7.5.4 _______________________________________________ dev mailing list [email protected] http://openvswitch.org/mailman/listinfo/dev
