diff --git a/include/openflow/openflow-1.4.h b/include/openflow/openflow-1.4.h index d912161a37f..4a18e1f3067 100644 --- a/include/openflow/openflow-1.4.h +++ b/include/openflow/openflow-1.4.h @@ -156,6 +156,55 @@ struct ofp14_table_mod { OFP_ASSERT(sizeof(struct ofp14_table_mod) == 8); +/* ## ---------------- ## */ +/* ## ofp14_port_stats ## */ +/* ## ---------------- ## */ + +enum ofp14_port_stats_prop_type { + OFPPSPT14_ETHERNET = 0, /* Ethernet property. */ + OFPPSPT14_OPTICAL = 1, /* Optical property. */ + OFPPSPT14_EXPERIMENTER = 0xFFFF, /* Experimenter property. */ +}; + +struct ofp14_port_stats_prop_ethernet { + ovs_be16 type; /* OFPPSPT14_ETHERNET. */ + ovs_be16 length; /* Length in bytes of this property. */ + uint8_t pad[4]; /* Align to 64 bits. */ + + ovs_be64 rx_frame_err; /* Number of frame alignment errors. */ + ovs_be64 rx_over_err; /* Number of packets with RX overrun. */ + ovs_be64 rx_crc_err; /* Number of CRC errors. */ + ovs_be64 collisions; /* Number of collisions. */ +}; +OFP_ASSERT(sizeof(struct ofp14_port_stats_prop_ethernet) == 40); + +struct ofp14_port_stats { + ovs_be16 length; /* Length of this entry. */ + uint8_t pad[2]; /* Align to 64 bits. */ + ovs_be32 port_no; + ovs_be32 duration_sec; /* Time port has been alive in seconds. */ + ovs_be32 duration_nsec; /* Time port has been alive in nanoseconds beyond + duration_sec. */ + ovs_be64 rx_packets; /* Number of received packets. */ + ovs_be64 tx_packets; /* Number of transmitted packets. */ + ovs_be64 rx_bytes; /* Number of received bytes. */ + ovs_be64 tx_bytes; /* Number of transmitted bytes. */ + + ovs_be64 rx_dropped; /* Number of packets dropped by RX. */ + ovs_be64 tx_dropped; /* Number of packets dropped by TX. */ + ovs_be64 rx_errors; /* Number of receive errors. This is a super-set + of more specific receive errors and should be + greater than or equal to the sum of all + rx_*_err values in properties. */ + ovs_be64 tx_errors; /* Number of transmit errors. This is a super-set + of more specific transmit errors and should be + greater than or equal to the sum of all + tx_*_err values (none currently defined.) */ + /* Followed by 0 or more OFPPSPT14_* properties. */ +}; +OFP_ASSERT(sizeof(struct ofp14_port_stats) == 80); + + /* ## -------------- ## */ /* ## Miscellaneous. ## */ /* ## -------------- ## */ diff --git a/lib/ofp-msgs.h b/lib/ofp-msgs.h index def24ce6f98..4685354bbd2 100644 --- a/lib/ofp-msgs.h +++ b/lib/ofp-msgs.h @@ -302,8 +302,10 @@ enum ofpraw { OFPRAW_OFPST10_PORT_REPLY, /* OFPST 1.1-1.2 (4): struct ofp11_port_stats[]. */ OFPRAW_OFPST11_PORT_REPLY, - /* OFPST 1.3+ (4): struct ofp13_port_stats[]. */ + /* OFPST 1.3 (4): struct ofp13_port_stats[]. */ OFPRAW_OFPST13_PORT_REPLY, + /* OFPST 1.4+ (4): uint8_t[8][]. */ + OFPRAW_OFPST14_PORT_REPLY, /* OFPST 1.0 (5): struct ofp10_queue_stats_request. */ OFPRAW_OFPST10_QUEUE_REQUEST, @@ -553,7 +555,8 @@ enum ofptype { * OFPRAW_OFPST11_PORT_REQUEST. */ OFPTYPE_PORT_STATS_REPLY, /* OFPRAW_OFPST10_PORT_REPLY. * OFPRAW_OFPST11_PORT_REPLY. - * OFPRAW_OFPST13_PORT_REPLY. */ + * OFPRAW_OFPST13_PORT_REPLY. + * OFPRAW_OFPST14_PORT_REPLY. */ OFPTYPE_QUEUE_STATS_REQUEST, /* OFPRAW_OFPST10_QUEUE_REQUEST. * OFPRAW_OFPST11_QUEUE_REQUEST. */ OFPTYPE_QUEUE_STATS_REPLY, /* OFPRAW_OFPST10_QUEUE_REPLY. diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 871d1ec4895..60d6ef1b498 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -6162,6 +6162,40 @@ ofputil_port_stats_to_ofp13(const struct ofputil_port_stats *ops, ps13->duration_nsec = htonl(ops->duration_nsec); } +static void +ofputil_append_ofp14_port_stats(const struct ofputil_port_stats *ops, + struct list *replies) +{ + struct ofp14_port_stats_prop_ethernet *eth; + struct ofp14_port_stats *ps14; + struct ofpbuf *reply; + + reply = ofpmp_reserve(replies, sizeof *ps14 + sizeof *eth); + + ps14 = ofpbuf_put_uninit(reply, sizeof *ps14); + ps14->length = htons(sizeof *ps14 + sizeof *eth); + memset(ps14->pad, 0, sizeof ps14->pad); + ps14->port_no = ofputil_port_to_ofp11(ops->port_no); + ps14->duration_sec = htonl(ops->duration_sec); + ps14->duration_nsec = htonl(ops->duration_nsec); + ps14->rx_packets = htonll(ops->stats.rx_packets); + ps14->tx_packets = htonll(ops->stats.tx_packets); + ps14->rx_bytes = htonll(ops->stats.rx_bytes); + ps14->tx_bytes = htonll(ops->stats.tx_bytes); + ps14->rx_dropped = htonll(ops->stats.rx_dropped); + ps14->tx_dropped = htonll(ops->stats.tx_dropped); + ps14->rx_errors = htonll(ops->stats.rx_errors); + ps14->tx_errors = htonll(ops->stats.tx_errors); + + eth = ofpbuf_put_uninit(reply, sizeof *eth); + eth->type = htons(OFPPSPT14_ETHERNET); + eth->length = htons(sizeof *eth); + memset(eth->pad, 0, sizeof eth->pad); + eth->rx_frame_err = htonll(ops->stats.rx_frame_errors); + eth->rx_over_err = htonll(ops->stats.rx_over_errors); + eth->rx_crc_err = htonll(ops->stats.rx_crc_errors); + eth->collisions = htonll(ops->stats.collisions); +} /* Encode a ports stat for 'ops' and append it to 'replies'. */ void @@ -6188,7 +6222,7 @@ ofputil_append_port_stat(struct list *replies, } case OFP14_VERSION: - OVS_NOT_REACHED(); + ofputil_append_ofp14_port_stats(ops, replies); break; default: @@ -6262,23 +6296,92 @@ ofputil_port_stats_from_ofp13(struct ofputil_port_stats *ops, return error; } -static size_t -ofputil_get_port_stats_size(enum ofp_version ofp_version) +static enum ofperr +parse_ofp14_port_stats_ethernet_property(const struct ofpbuf *payload, + struct ofputil_port_stats *ops) { - switch (ofp_version) { - case OFP10_VERSION: - return sizeof(struct ofp10_port_stats); - case OFP11_VERSION: - case OFP12_VERSION: - return sizeof(struct ofp11_port_stats); - case OFP13_VERSION: - return sizeof(struct ofp13_port_stats); - case OFP14_VERSION: - OVS_NOT_REACHED(); - return 0; - default: - OVS_NOT_REACHED(); + const struct ofp14_port_stats_prop_ethernet *eth = ofpbuf_data(payload); + + if (ofpbuf_size(payload) != sizeof *eth) { + return OFPERR_OFPBPC_BAD_LEN; + } + + ops->stats.rx_frame_errors = ntohll(eth->rx_frame_err); + ops->stats.rx_over_errors = ntohll(eth->rx_over_err); + ops->stats.rx_crc_errors = ntohll(eth->rx_crc_err); + ops->stats.collisions = ntohll(eth->collisions); + + return 0; +} + +static enum ofperr +ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops, + struct ofpbuf *msg) +{ + const struct ofp14_port_stats *ps14; + struct ofpbuf properties; + enum ofperr error; + size_t len; + + ps14 = ofpbuf_try_pull(msg, sizeof *ps14); + if (!ps14) { + return OFPERR_OFPBRC_BAD_LEN; + } + + len = ntohs(ps14->length); + if (len < sizeof *ps14 || len - sizeof *ps14 > ofpbuf_size(msg)) { + return OFPERR_OFPBRC_BAD_LEN; } + len -= sizeof *ps14; + ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len); + + error = ofputil_port_from_ofp11(ps14->port_no, &ops->port_no); + if (error) { + return error; + } + + ops->duration_sec = ntohl(ps14->duration_sec); + ops->duration_nsec = ntohl(ps14->duration_nsec); + ops->stats.rx_packets = ntohll(ps14->rx_packets); + ops->stats.tx_packets = ntohll(ps14->tx_packets); + ops->stats.rx_bytes = ntohll(ps14->rx_bytes); + ops->stats.tx_bytes = ntohll(ps14->tx_bytes); + ops->stats.rx_dropped = ntohll(ps14->rx_dropped); + ops->stats.tx_dropped = ntohll(ps14->tx_dropped); + ops->stats.rx_errors = ntohll(ps14->rx_errors); + ops->stats.tx_errors = ntohll(ps14->tx_errors); + ops->stats.rx_frame_errors = UINT64_MAX; + ops->stats.rx_over_errors = UINT64_MAX; + ops->stats.rx_crc_errors = UINT64_MAX; + ops->stats.collisions = UINT64_MAX; + + while (ofpbuf_size(&properties) > 0) { + struct ofpbuf payload; + enum ofperr error; + uint16_t type; + + error = ofputil_pull_property(&properties, &payload, &type); + if (error) { + return error; + } + + switch (type) { + case OFPPSPT14_ETHERNET: + error = parse_ofp14_port_stats_ethernet_property(&payload, ops); + break; + + default: + log_property(true, "unknown port stats property %"PRIu16, type); + error = 0; + break; + } + + if (error) { + return error; + } + } + + return 0; } /* Returns the number of port stats elements in OFPTYPE_PORT_STATS_REPLY @@ -6286,12 +6389,16 @@ ofputil_get_port_stats_size(enum ofp_version ofp_version) size_t ofputil_count_port_stats(const struct ofp_header *oh) { + struct ofputil_port_stats ps; struct ofpbuf b; + size_t n = 0; ofpbuf_use_const(&b, oh, ntohs(oh->length)); ofpraw_pull_assert(&b); - - return ofpbuf_size(&b) / ofputil_get_port_stats_size(oh->version); + while (!ofputil_decode_port_stats(&ps, &b)) { + n++; + } + return n; } /* Converts an OFPST_PORT_STATS reply in 'msg' into an abstract @@ -6319,6 +6426,8 @@ ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg) if (!ofpbuf_size(msg)) { return EOF; + } else if (raw == OFPRAW_OFPST14_PORT_REPLY) { + return ofputil_pull_ofp14_port_stats(ps, msg); } else if (raw == OFPRAW_OFPST13_PORT_REPLY) { const struct ofp13_port_stats *ps13; @@ -6361,6 +6470,7 @@ ofputil_decode_port_stats_request(const struct ofp_header *request, ofp_port_t *ofp10_port) { switch ((enum ofp_version)request->version) { + case OFP14_VERSION: case OFP13_VERSION: case OFP12_VERSION: case OFP11_VERSION: { @@ -6374,10 +6484,6 @@ ofputil_decode_port_stats_request(const struct ofp_header *request, return 0; } - case OFP14_VERSION: - OVS_NOT_REACHED(); - break; - default: OVS_NOT_REACHED(); } diff --git a/tests/ofp-print.at b/tests/ofp-print.at index 5ef959e196e..e90d5bb5a80 100644 --- a/tests/ofp-print.at +++ b/tests/ofp-print.at @@ -1616,6 +1616,26 @@ OFPST_PORT reply (OF1.3) (xid=0x2): 3 ports ]) AT_CLEANUP +AT_SETUP([OFPST_PORT reply - OF1.4]) +AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) +AT_CHECK([ovs-ofctl ofp-print "\ +05 13 00 88 00 00 00 02 00 04 00 00 00 00 00 00 \ +00 78 00 00 00 00 00 02 00 00 00 01 00 0f 42 40 \ +00 00 00 00 00 01 95 56 00 00 00 00 00 00 00 88 \ +00 00 00 00 02 5d 08 98 00 00 00 00 00 00 2c f8 \ +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ +00 00 00 28 00 00 00 00 00 00 00 00 00 00 00 fc \ +00 00 00 00 00 00 00 fd 00 00 00 00 00 00 00 fe \ +00 00 00 00 00 00 00 ff \ +"], [0], [dnl +OFPST_PORT reply (OF1.4) (xid=0x2): 1 ports + port 2: rx pkts=103766, bytes=39651480, drop=0, errs=0, frame=252, over=253, crc=254 + tx pkts=136, bytes=11512, drop=0, errs=0, coll=255 + duration=1.001s +]) +AT_CLEANUP + AT_SETUP([OFPST_QUEUE request - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ diff --git a/tests/ofproto.at b/tests/ofproto.at index b9c6d28673a..4479c381417 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -101,6 +101,19 @@ OFPST_PORT reply (OF1.2): 1 ports OVS_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([ofproto - port stats - (OpenFlow 1.4)]) +OVS_VSWITCHD_START +AT_CHECK([ovs-ofctl -O OpenFlow14 -vwarn dump-ports br0], [0], [stdout]) +AT_CHECK([STRIP_XIDS stdout | sed 's/duration=[[0-9.]]*s/duration=?s/'], + [0], [dnl +OFPST_PORT reply (OF1.4): 1 ports + port LOCAL: rx pkts=0, bytes=0, drop=0, errs=0, frame=0, over=0, crc=0 + tx pkts=0, bytes=0, drop=0, errs=0, coll=0 + duration=?s +]) +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 - port-desc stats (OpenFlow 1.0)])