From abaad8cf1b5089e17a4af0ab1ff644bfcf63cad9 Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Tue, 19 Jan 2010 22:35:18 -0800 Subject: [PATCH] ofproto: Querying port stats for individual ports (OpenFlow 1.0) OpenFlow 1.0 adds "port_no" field to the Port Stat request messages to allow stats for individual ports to be queried. Port stats for all ports can still be requested by specifying OFPP_NONE as the port number. NOTE: OVS at this point is not wire-compatible with OpenFlow 1.0 until the final commit in this OpenFlow 1.0 set. --- extras/ezio/ovs-switchui.c | 5 +++ include/openflow/openflow.h | 11 +++++- lib/ofp-print.c | 12 +++++- ofproto/ofproto.c | 75 ++++++++++++++++++++++++------------- utilities/ovs-ofctl.8.in | 8 ++-- utilities/ovs-ofctl.c | 57 ++++++++++++++++++++++++++-- 6 files changed, 133 insertions(+), 35 deletions(-) diff --git a/extras/ezio/ovs-switchui.c b/extras/ezio/ovs-switchui.c index 79f5552d..67f97476 100644 --- a/extras/ezio/ovs-switchui.c +++ b/extras/ezio/ovs-switchui.c @@ -1105,6 +1105,7 @@ do_show_data_rates(void *rates_) } if (!rates->xid) { struct ofp_stats_request *rq; + struct ofp_port_stats_request *psr; struct ofpbuf *b; rates->xid = random_uint32(); @@ -1112,6 +1113,10 @@ do_show_data_rates(void *rates_) rates->xid, &b); rq->type = htons(OFPST_PORT); rq->flags = htons(0); + psr = ofbuf_put_uninit(b, sizeof *psr); + memset(psr, 0, sizeof *psr); + psr->port_no = htons(OFPP_NONE); + update_openflow_length(b); rconn_send_with_limit(rates->rconn, b, counter, 10); } diff --git a/include/openflow/openflow.h b/include/openflow/openflow.h index 87f9f2cf..d3a98dc5 100644 --- a/include/openflow/openflow.h +++ b/include/openflow/openflow.h @@ -693,7 +693,7 @@ enum ofp_stats_types { OFPST_TABLE, /* Physical port statistics. - * The request body is empty. + * The request body is struct ofp_port_stats_request. * The reply body is an array of struct ofp_port_stats. */ OFPST_PORT, @@ -807,6 +807,15 @@ struct ofp_table_stats { }; OFP_ASSERT(sizeof(struct ofp_table_stats) == 64); +/* Body for ofp_stats_request of type OFPST_PORT. */ +struct ofp_port_stats_request { + uint16_t port_no; /* OFPST_PORT message may request statistics + for a single port (specified with port_no) + or for all ports (port_no == OFPP_NONE). */ + uint8_t pad[6]; +}; +OFP_ASSERT(sizeof(struct ofp_port_stats_request) == 8); + /* Body of reply to OFPST_PORT request. If a counter is unsupported, set * the field to all ones. */ struct ofp_port_stats { diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 90afec48..5d06b96f 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -1063,6 +1063,14 @@ static void print_port_stat(struct ds *string, const char *leader, } } +static void +ofp_port_stats_request(struct ds *string, const void *body_, + size_t len OVS_UNUSED, int verbosity OVS_UNUSED) +{ + const struct ofp_port_stats_request *psr = body_; + ds_put_format(string, "port_no=%"PRIu16, ntohs(psr->port_no)); +} + static void ofp_port_stats_reply(struct ds *string, const void *body, size_t len, int verbosity) @@ -1187,7 +1195,9 @@ print_stats(struct ds *string, int type, const void *body, size_t body_len, { OFPST_PORT, "port", - { 0, 0, NULL, }, + { sizeof(struct ofp_port_stats_request), + sizeof(struct ofp_port_stats_request), + ofp_port_stats_request }, { 0, SIZE_MAX, ofp_port_stats_reply }, }, { diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 804e396c..00c9c696 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -2458,39 +2458,62 @@ handle_table_stats_request(struct ofproto *p, struct ofconn *ofconn, return 0; } +static void +append_port_stat(struct ofport *port, uint16_t port_no, struct ofconn *ofconn, + struct ofpbuf *msg) +{ + struct netdev_stats stats; + struct ofp_port_stats *ops; + + /* Intentionally ignore return value, since errors will set + * 'stats' to all-1s, which is correct for OpenFlow, and + * netdev_get_stats() will log errors. */ + netdev_get_stats(port->netdev, &stats); + + ops = append_stats_reply(sizeof *ops, ofconn, &msg); + ops->port_no = htons(odp_port_to_ofp_port(port_no)); + memset(ops->pad, 0, sizeof ops->pad); + ops->rx_packets = htonll(stats.rx_packets); + ops->tx_packets = htonll(stats.tx_packets); + ops->rx_bytes = htonll(stats.rx_bytes); + ops->tx_bytes = htonll(stats.tx_bytes); + ops->rx_dropped = htonll(stats.rx_dropped); + ops->tx_dropped = htonll(stats.tx_dropped); + ops->rx_errors = htonll(stats.rx_errors); + ops->tx_errors = htonll(stats.tx_errors); + ops->rx_frame_err = htonll(stats.rx_frame_errors); + ops->rx_over_err = htonll(stats.rx_over_errors); + ops->rx_crc_err = htonll(stats.rx_crc_errors); + ops->collisions = htonll(stats.collisions); +} + static int handle_port_stats_request(struct ofproto *p, struct ofconn *ofconn, - struct ofp_stats_request *request) + struct ofp_stats_request *osr, + size_t arg_size) { + struct ofp_port_stats_request *psr; struct ofp_port_stats *ops; struct ofpbuf *msg; struct ofport *port; unsigned int port_no; - msg = start_stats_reply(request, sizeof *ops * 16); - PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) { - struct netdev_stats stats; - - /* Intentionally ignore return value, since errors will set 'stats' to - * all-1s, which is correct for OpenFlow, and netdev_get_stats() will - * log errors. */ - netdev_get_stats(port->netdev, &stats); - - ops = append_stats_reply(sizeof *ops, ofconn, &msg); - ops->port_no = htons(odp_port_to_ofp_port(port_no)); - memset(ops->pad, 0, sizeof ops->pad); - ops->rx_packets = htonll(stats.rx_packets); - ops->tx_packets = htonll(stats.tx_packets); - ops->rx_bytes = htonll(stats.rx_bytes); - ops->tx_bytes = htonll(stats.tx_bytes); - ops->rx_dropped = htonll(stats.rx_dropped); - ops->tx_dropped = htonll(stats.tx_dropped); - ops->rx_errors = htonll(stats.rx_errors); - ops->tx_errors = htonll(stats.tx_errors); - ops->rx_frame_err = htonll(stats.rx_frame_errors); - ops->rx_over_err = htonll(stats.rx_over_errors); - ops->rx_crc_err = htonll(stats.rx_crc_errors); - ops->collisions = htonll(stats.collisions); + if (arg_size != sizeof *psr) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + psr = (struct ofp_port_stats_request *) osr->body; + + msg = start_stats_reply(osr, sizeof *ops * 16); + if (psr->port_no != htons(OFPP_NONE)) { + port = port_array_get(&p->ports, + ofp_port_to_odp_port(ntohs(psr->port_no))); + if (port) { + append_port_stat(port, ntohs(psr->port_no), ofconn, msg); + } + } else { + PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) { + append_port_stat(port, port_no, ofconn, msg); + } } queue_tx(msg, ofconn, ofconn->reply_counter); @@ -2763,7 +2786,7 @@ handle_stats_request(struct ofproto *p, struct ofconn *ofconn, return handle_table_stats_request(p, ofconn, osr); case OFPST_PORT: - return handle_port_stats_request(p, ofconn, osr); + return handle_port_stats_request(p, ofconn, osr, arg_size); case OFPST_VENDOR: return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR); diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 15be8e98..0faed9c9 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -60,9 +60,11 @@ Prints to the console statistics for each of the flow tables used by \fIswitch\fR. .TP -\fBdump-ports \fIswitch\fR -Prints to the console statistics for each of the network devices -associated with \fIswitch\fR. +\fBdump-ports \fIswitch\fR [\fInetdev\fR] +Prints to the console statistics for network devices associated with +\fIswitch\fR. If \fInetdev\fR is specified, only the statistics +associated with that device will be printed. \fInetdev\fR can be an +OpenFlow assigned port number or device name, e.g. \fBeth0\fR. .TP \fBmod-port \fIswitch\fR \fInetdev\fR \fIaction\fR diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 852d542e..69f8adfd 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -153,7 +153,7 @@ usage(void) " dump-desc SWITCH print switch description\n" " dump-tables SWITCH print table stats\n" " mod-port SWITCH IFACE ACT modify port behavior\n" - " dump-ports SWITCH print port statistics\n" + " dump-ports SWITCH [PORT] print port statistics\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" @@ -469,6 +469,48 @@ str_to_ip(const char *str_, uint32_t *ip) return n_wild; } +static uint16_t +str_to_port_no(const char *vconn_name, const char *str) +{ + struct ofpbuf *request, *reply; + struct ofp_switch_features *osf; + struct vconn *vconn; + int n_ports; + int port_idx; + unsigned int port_no; + + + /* Check if the argument is a port index. Otherwise, treat it as + * the port name. */ + if (str_to_uint(str, 10, &port_no)) { + return port_no; + } + + /* Send a "Features Request" to resolve the name into a number. */ + make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &request); + open_vconn(vconn_name, &vconn); + run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name); + + osf = reply->data; + n_ports = (reply->size - sizeof *osf) / sizeof *osf->ports; + + for (port_idx = 0; port_idx < n_ports; port_idx++) { + /* Check argument as an interface name */ + if (!strncmp((char *)osf->ports[port_idx].name, str, + sizeof osf->ports[0].name)) { + break; + } + } + if (port_idx == n_ports) { + ovs_fatal(0, "couldn't find monitored port: %s", str); + } + + ofpbuf_delete(reply); + vconn_close(vconn); + + return port_idx; +} + static void * put_action(struct ofpbuf *b, size_t size, uint16_t type) { @@ -995,9 +1037,16 @@ do_monitor(int argc OVS_UNUSED, char *argv[]) } static void -do_dump_ports(int argc OVS_UNUSED, char *argv[]) +do_dump_ports(int argc, char *argv[]) { - dump_trivial_stats_transaction(argv[1], OFPST_PORT); + struct ofp_port_stats_request *req; + struct ofpbuf *request; + uint16_t port; + + req = alloc_stats_request(sizeof *req, OFPST_PORT, &request); + port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_NONE; + req->port_no = htons(port); + dump_stats_transaction(argv[1], request); } static void @@ -1205,7 +1254,7 @@ static const struct command all_commands[] = { { "add-flows", 2, 2, do_add_flows }, { "mod-flows", 2, 2, do_mod_flows }, { "del-flows", 1, 2, do_del_flows }, - { "dump-ports", 1, 1, do_dump_ports }, + { "dump-ports", 1, 2, do_dump_ports }, { "mod-port", 3, 3, do_mod_port }, { "probe", 1, 1, do_probe }, { "ping", 1, 2, do_ping }, -- 2.30.2