Skip to content

Commit

Permalink
Implement OpenFlow 1.4 port statistics.
Browse files Browse the repository at this point in the history
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
blp committed May 14, 2014
1 parent 37ab26e commit 5469537
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 24 deletions.
49 changes: 49 additions & 0 deletions include/openflow/openflow-1.4.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. ## */
/* ## -------------- ## */
Expand Down
7 changes: 5 additions & 2 deletions lib/ofp-msgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
150 changes: 128 additions & 22 deletions lib/ofp-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -6262,36 +6296,109 @@ 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
* message 'oh'. */
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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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: {
Expand All @@ -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();
}
Expand Down
20 changes: 20 additions & 0 deletions tests/ofp-print.at
Original file line number Diff line number Diff line change
Expand Up @@ -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 "\
Expand Down
13 changes: 13 additions & 0 deletions tests/ofproto.at
Original file line number Diff line number Diff line change
Expand Up @@ -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)])
Expand Down

0 comments on commit 5469537

Please sign in to comment.