[ovs-dev] [of1.5 v2 10/20] Implement OpenFlow 1.4 port statistics.

Ben Pfaff blp at nicira.com
Sat May 10 02:40:26 UTC 2014


Signed-off-by: Ben Pfaff <blp at nicira.com>
---
 include/openflow/openflow-1.4.h |  49 +++++++++++++
 lib/ofp-msgs.h                  |   7 +-
 lib/ofp-util.c                  | 150 ++++++++++++++++++++++++++++++++++------
 tests/ofp-print.at              |  20 ++++++
 tests/ofproto.at                |  13 ++++
 5 files changed, 215 insertions(+), 24 deletions(-)

diff --git a/include/openflow/openflow-1.4.h b/include/openflow/openflow-1.4.h
index d912161..4a18e1f 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 def24ce..4685354 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 f77215a..1728352 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -6159,6 +6159,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
@@ -6185,7 +6219,7 @@ ofputil_append_port_stat(struct list *replies,
     }
 
     case OFP14_VERSION:
-        OVS_NOT_REACHED();
+        ofputil_append_ofp14_port_stats(ops, replies);
         break;
 
     default:
@@ -6259,23 +6293,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
@@ -6283,12 +6386,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
@@ -6316,6 +6423,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;
 
@@ -6358,6 +6467,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: {
@@ -6371,10 +6481,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 5ef959e..e90d5bb 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 b9c6d28..4479c38 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)])
-- 
1.9.1




More information about the dev mailing list