summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSatyaValli <satyavalli.rama@tcs.com>2018-05-10 21:56:54 +0530
committerBen Pfaff <blp@ovn.org>2018-05-16 15:29:46 -0700
commitc7b02b800615c0bf383f3a740fe62b3f1759a997 (patch)
treecbdf95ba2890c3ef54926ac44efaa5ac35932133
parent7c0cb293eea321b934ff6398dd7e938ce3a0511f (diff)
downloadopenvswitch-c7b02b800615c0bf383f3a740fe62b3f1759a997.tar.gz
Add support for OpenFlow 1.5 statistics (OXS).
This patch provides implementation Existing flow entry statistics are redefined as standard OXS(OpenFlow Extensible Statistics) fields for displaying the arbitrary flow stats. To support this implementation below messages are newly added OFPRAW_OFPT15_FLOW_REMOVED, OFPRAW_OFPST15_AGGREGATE_REQUEST, OFPRAW_OFPST15_FLOW_REPLY, OFPRAW_OFPST15_AGGREGATE_REPLY, The current commit adds support for the new feature in flow statistics multipart messages, aggregate multipart messages and OXS support for flow removal message, individual flow description messages. Signed-off-by: Satya Valli <satyavalli.rama@tcs.com> Co-authored-by: Lavanya Harivelam <harivelam.lavanya@tcs.com> Signed-off-by: Lavanya Harivelam <harivelam.lavanya@tcs.com> Co-authored-by: Surya Muttamsetty <muttamsetty.surya@tcs.com> Signed-off-by: Surya Muttamsetty <muttamsetty.surya@tcs.com> Co-authored-by: Manasa Cherukupally <manasa.cherukupally@tcs.com> Signed-off-by: Manasa Cherukupally <manasa.cherukupally@tcs.com> Co-authored-by: Pavani Panthagada <p.pavani1@tcs.com> Signed-off-by: Pavani Panthagada <p.pavani1@tcs.com> [blp@ovn.org simplified and rewrote much of the code] Co-authored-by: Ben Pfaff <blp@ovn.org> Signed-off-by: Ben Pfaff <blp@ovn.org>
-rw-r--r--NEWS1
-rw-r--r--include/openflow/openflow-1.5.h51
-rw-r--r--include/openvswitch/ofp-flow.h2
-rw-r--r--include/openvswitch/ofp-monitor.h4
-rw-r--r--include/openvswitch/ofp-msgs.h20
-rw-r--r--lib/automake.mk2
-rw-r--r--lib/ofp-flow.c182
-rw-r--r--lib/ofp-monitor.c63
-rw-r--r--lib/ox-stat.c355
-rw-r--r--lib/ox-stat.h37
-rw-r--r--tests/ofp-print.at80
-rw-r--r--tests/ofproto-dpif.at85
-rw-r--r--tests/ofproto.at1
13 files changed, 847 insertions, 36 deletions
diff --git a/NEWS b/NEWS
index 328d6ecb9..cb27aaedf 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,7 @@ Post-v2.9.0
- ovs-vsctl: New commands "add-bond-iface" and "del-bond-iface".
- OpenFlow:
* OFPT_ROLE_STATUS is now available in OpenFlow 1.3.
+ * OpenFlow 1.5 extensibile statistics (OXS) now implemented.
- Linux kernel 4.14
* Add support for compiling OVS with the latest Linux 4.14 kernel
- ovn:
diff --git a/include/openflow/openflow-1.5.h b/include/openflow/openflow-1.5.h
index 73b76d883..d9b867cf2 100644
--- a/include/openflow/openflow-1.5.h
+++ b/include/openflow/openflow-1.5.h
@@ -163,4 +163,55 @@ struct ofp15_packet_out {
};
OFP_ASSERT(sizeof(struct ofp15_packet_out) == 8);
+/* Body of reply to OFPMP_FLOW_DESC request. */
+struct ofp15_flow_desc {
+ ovs_be16 length; /* Length of this entry. */
+ uint8_t pad2[2]; /* Align to 64 bits. */
+ uint8_t table_id; /* ID of table flow came from. */
+ uint8_t pad;
+ ovs_be16 priority; /* Priority of the entry. */
+ ovs_be16 idle_timeout; /* Number of seconds
+ idle before expiration. */
+ ovs_be16 hard_timeout; /* Number of seconds
+ before expiration. */
+ ovs_be16 flags; /* Bitmap of OFPFF_*. flags. */
+ ovs_be16 importance; /* Eviction precedence. */
+ ovs_be64 cookie; /* Opaque controller issued identifier. */
+};
+
+OFP_ASSERT(sizeof(struct ofp15_flow_desc) == 24);
+
+/* Body of reply to OFPMP_FLOW_STATS request
+ * and body for OFPIT_STAT_TRIGGER generated status. */
+struct ofp15_flow_stats_reply {
+ ovs_be16 length; /* Length of this entry. */
+ uint8_t pad2[2]; /* Align to 64 bits. */
+ uint8_t table_id; /* ID of table flow came from. */
+ uint8_t reason; /* One of OFPFSR_*. */
+ ovs_be16 priority; /* Priority of the entry. */
+};
+
+OFP_ASSERT(sizeof(struct ofp15_flow_stats_reply) == 8);
+
+/* OXS flow stat field types for OpenFlow basic class. */
+enum oxs_ofb_stat_fields {
+ OFPXST_OFB_DURATION = 0, /* Time flow entry has been alive. */
+ OFPXST_OFB_IDLE_TIME = 1, /* Time flow entry has been idle. */
+ OFPXST_OFB_FLOW_COUNT = 3, /* Number of aggregated flow entries. */
+ OFPXST_OFB_PACKET_COUNT = 4, /* Number of packets in flow entry. */
+ OFPXST_OFB_BYTE_COUNT = 5, /* Number of bytes in flow entry. */
+};
+
+/* Flow removed (datapath -> controller). */
+struct ofp15_flow_removed {
+ uint8_t table_id; /* ID of the table */
+ uint8_t reason; /* One of OFPRR_*. */
+ ovs_be16 priority; /* Priority level of flow entry. */
+ ovs_be16 idle_timeout; /* Idle timeout from original flow mod. */
+ ovs_be16 hard_timeout; /* Hard timeout from original flow mod. */
+ ovs_be64 cookie; /* Opaque controller issued identifier. */
+};
+
+OFP_ASSERT(sizeof (struct ofp15_flow_removed) == 16);
+
#endif /* openflow/openflow-1.5.h */
diff --git a/include/openvswitch/ofp-flow.h b/include/openvswitch/ofp-flow.h
index 2ff2e45b6..f2223d90b 100644
--- a/include/openvswitch/ofp-flow.h
+++ b/include/openvswitch/ofp-flow.h
@@ -219,7 +219,7 @@ void ofputil_flow_stats_format(struct ds *, const struct ofputil_flow_stats *,
struct ofputil_aggregate_stats {
uint64_t packet_count; /* Packet count, UINT64_MAX if unknown. */
uint64_t byte_count; /* Byte count, UINT64_MAX if unknown. */
- uint32_t flow_count;
+ uint32_t flow_count; /* Number of flows, UINT32_MAX if unknown. */
};
struct ofpbuf *ofputil_encode_aggregate_stats_reply(
diff --git a/include/openvswitch/ofp-monitor.h b/include/openvswitch/ofp-monitor.h
index bd225518e..47d0d0e89 100644
--- a/include/openvswitch/ofp-monitor.h
+++ b/include/openvswitch/ofp-monitor.h
@@ -39,8 +39,8 @@ struct ofputil_flow_removed {
uint16_t priority;
uint8_t reason; /* One of OFPRR_*. */
uint8_t table_id; /* 255 if message didn't include table ID. */
- uint32_t duration_sec;
- uint32_t duration_nsec;
+ uint32_t duration_sec; /* Duration in sec, UINT32_MAX if unknown. */
+ uint32_t duration_nsec; /* Fractional duration in nsec. */
uint16_t idle_timeout;
uint16_t hard_timeout;
uint64_t packet_count; /* Packet count, UINT64_MAX if unknown. */
diff --git a/include/openvswitch/ofp-msgs.h b/include/openvswitch/ofp-msgs.h
index 5998c4c22..ea2fa5bb0 100644
--- a/include/openvswitch/ofp-msgs.h
+++ b/include/openvswitch/ofp-msgs.h
@@ -165,8 +165,10 @@ enum ofpraw {
/* OFPT 1.0 (11): struct ofp10_flow_removed. */
OFPRAW_OFPT10_FLOW_REMOVED,
- /* OFPT 1.1+ (11): struct ofp11_flow_removed, uint8_t[8][]. */
+ /* OFPT 1.1-1.4 (11): struct ofp11_flow_removed, uint8_t[8][]. */
OFPRAW_OFPT11_FLOW_REMOVED,
+ /* OFPT 1.5+ (11): struct ofp15_flow_removed, uint8_t[8][]. */
+ OFPRAW_OFPT15_FLOW_REMOVED,
/* NXT 1.0+ (14): struct nx_flow_removed, uint8_t[8][]. */
OFPRAW_NXT_FLOW_REMOVED,
@@ -302,20 +304,26 @@ enum ofpraw {
OFPRAW_OFPST10_FLOW_REPLY,
/* OFPST 1.1-1.2 (1): uint8_t[]. */
OFPRAW_OFPST11_FLOW_REPLY,
- /* OFPST 1.3+ (1): uint8_t[]. */
+ /* OFPST 1.3-1.4 (1): uint8_t[]. */
OFPRAW_OFPST13_FLOW_REPLY,
+ /* OFPST 1.5+ (1): uint8_t[]. */
+ OFPRAW_OFPST15_FLOW_REPLY,
/* NXST 1.0 (0): uint8_t[]. */
OFPRAW_NXST_FLOW_REPLY,
/* OFPST 1.0 (2): struct ofp10_flow_stats_request. */
OFPRAW_OFPST10_AGGREGATE_REQUEST,
- /* OFPST 1.1+ (2): struct ofp11_flow_stats_request, uint8_t[8][]. */
+ /* OFPST 1.1-1.4 (2): struct ofp11_flow_stats_request, uint8_t[8][]. */
OFPRAW_OFPST11_AGGREGATE_REQUEST,
+ /* OFPST 1.5+ (2): struct ofp11_flow_stats_request, uint8_t[8][]. */
+ OFPRAW_OFPST15_AGGREGATE_REQUEST,
/* NXST 1.0 (1): struct nx_flow_stats_request, uint8_t[8][]. */
OFPRAW_NXST_AGGREGATE_REQUEST,
- /* OFPST 1.0+ (2): struct ofp_aggregate_stats_reply. */
+ /* OFPST 1.0-1.4 (2): struct ofp_aggregate_stats_reply. */
OFPRAW_OFPST_AGGREGATE_REPLY,
+ /* OFPST 1.5+ (2): uint8_t[] . */
+ OFPRAW_OFPST15_AGGREGATE_REPLY,
/* NXST 1.0 (1): struct ofp_aggregate_stats_reply. */
OFPRAW_NXST_AGGREGATE_REPLY,
@@ -561,6 +569,7 @@ enum ofptype {
* OFPRAW_NXT_PACKET_IN. */
OFPTYPE_FLOW_REMOVED, /* OFPRAW_OFPT10_FLOW_REMOVED.
* OFPRAW_OFPT11_FLOW_REMOVED.
+ * OFPRAW_OFPT15_FLOW_REMOVED.
* OFPRAW_NXT_FLOW_REMOVED. */
OFPTYPE_PORT_STATUS, /* OFPRAW_OFPT10_PORT_STATUS.
* OFPRAW_OFPT11_PORT_STATUS.
@@ -641,11 +650,14 @@ enum ofptype {
OFPTYPE_FLOW_STATS_REPLY, /* OFPRAW_OFPST10_FLOW_REPLY.
* OFPRAW_OFPST11_FLOW_REPLY.
* OFPRAW_OFPST13_FLOW_REPLY.
+ * OFPRAW_OFPST15_FLOW_REPLY.
* OFPRAW_NXST_FLOW_REPLY. */
OFPTYPE_AGGREGATE_STATS_REQUEST, /* OFPRAW_OFPST10_AGGREGATE_REQUEST.
* OFPRAW_OFPST11_AGGREGATE_REQUEST.
+ * OFPRAW_OFPST15_AGGREGATE_REQUEST.
* OFPRAW_NXST_AGGREGATE_REQUEST. */
OFPTYPE_AGGREGATE_STATS_REPLY, /* OFPRAW_OFPST_AGGREGATE_REPLY.
+ * OFPRAW_OFPST15_AGGREGATE_REPLY.
* OFPRAW_NXST_AGGREGATE_REPLY. */
OFPTYPE_TABLE_STATS_REQUEST, /* OFPRAW_OFPST_TABLE_REQUEST. */
OFPTYPE_TABLE_STATS_REPLY, /* OFPRAW_OFPST10_TABLE_REPLY.
diff --git a/lib/automake.mk b/lib/automake.mk
index 0974b4242..fb781e847 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -219,6 +219,8 @@ lib_libopenvswitch_la_SOURCES = \
lib/ovsdb-session.h \
lib/ovsdb-types.c \
lib/ovsdb-types.h \
+ lib/ox-stat.c \
+ lib/ox-stat.h \
lib/packets.c \
lib/packets.h \
lib/pcap-file.c \
diff --git a/lib/ofp-flow.c b/lib/ofp-flow.c
index 3744cdbd8..9fbe8c2e5 100644
--- a/lib/ofp-flow.c
+++ b/lib/ofp-flow.c
@@ -32,6 +32,7 @@
#include "openvswitch/ofpbuf.h"
#include "openvswitch/vlog.h"
#include "util.h"
+#include "ox-stat.h"
VLOG_DEFINE_THIS_MODULE(ofp_flow);
@@ -707,6 +708,10 @@ ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
return ofputil_decode_ofpst11_flow_request(fsr, &b, true, tun_table,
vl_mff_map);
+ case OFPRAW_OFPST15_AGGREGATE_REQUEST:
+ return ofputil_decode_ofpst11_flow_request(fsr, &b, true,
+ tun_table, vl_mff_map);
+
case OFPRAW_NXST_FLOW_REQUEST:
return ofputil_decode_nxst_flow_request(fsr, &b, false, tun_table,
vl_mff_map);
@@ -740,9 +745,15 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
case OFPUTIL_P_OF16_OXM: {
struct ofp11_flow_stats_request *ofsr;
- raw = (fsr->aggregate
- ? OFPRAW_OFPST11_AGGREGATE_REQUEST
- : OFPRAW_OFPST11_FLOW_REQUEST);
+ if (protocol > OFPUTIL_P_OF14_OXM) {
+ raw = (fsr->aggregate
+ ? OFPRAW_OFPST15_AGGREGATE_REQUEST
+ : OFPRAW_OFPST11_FLOW_REQUEST);
+ } else {
+ raw = (fsr->aggregate
+ ? OFPRAW_OFPST11_AGGREGATE_REQUEST
+ : OFPRAW_OFPST11_FLOW_REQUEST);
+ }
msg = ofpraw_alloc(raw, ofputil_protocol_to_ofp_version(protocol),
ofputil_match_typical_len(protocol));
ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
@@ -894,6 +905,58 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
if (!msg->size) {
return EOF;
+ } else if (raw == OFPRAW_OFPST15_FLOW_REPLY) {
+ const struct ofp15_flow_desc *ofd;
+ size_t length;
+ uint16_t padded_match_len;
+ uint16_t stat_len;
+ uint8_t oxs_field_set;
+
+ ofd = ofpbuf_try_pull(msg, sizeof *ofd);
+ if (!ofd) {
+ VLOG_WARN_RL(&rl, "OFPST_FLOW reply has %" PRIu32
+ " leftover " "bytes at end", msg->size);
+ return EINVAL;
+ }
+
+ length = ntohs(ofd->length);
+ if (length < sizeof *ofd) {
+ VLOG_WARN_RL(&rl, "OFPST_FLOW reply claims invalid "
+ "length %" PRIuSIZE, length);
+ return EINVAL;
+ }
+
+ if (ofputil_pull_ofp11_match(msg, NULL, NULL, &fs->match,
+ &padded_match_len)) {
+ VLOG_WARN_RL(&rl, "OFPST_FLOW reply bad match");
+ return EINVAL;
+ }
+
+ fs->priority = ntohs(ofd->priority);
+ fs->table_id = ofd->table_id;
+ fs->cookie = ofd->cookie;
+ fs->idle_timeout = ntohs(ofd->idle_timeout);
+ fs->hard_timeout = ntohs(ofd->hard_timeout);
+ fs->importance = ntohs(ofd->importance);
+
+ error = ofputil_decode_flow_mod_flags(ofd->flags, -1, oh->version,
+ &fs->flags);
+ if (error) {
+ return error;
+ }
+
+ struct oxs_stats oxs;
+ if (oxs_pull_stat(msg, &oxs, &stat_len, &oxs_field_set)) {
+ VLOG_WARN_RL(&rl, "OXS OFPST_FLOW reply bad stats");
+ return EINVAL;
+ }
+ fs->duration_sec = oxs.duration_sec;
+ fs->duration_nsec = oxs.duration_nsec;
+ fs->packet_count = oxs.packet_count;
+ fs->byte_count = oxs.byte_count;
+ fs->idle_age = oxs.idle_age == UINT32_MAX ? -1 : oxs.idle_age;
+
+ instructions_len = length - sizeof *ofd - padded_match_len - stat_len;
} else if (raw == OFPRAW_OFPST11_FLOW_REPLY
|| raw == OFPRAW_OFPST13_FLOW_REPLY) {
const struct ofp11_flow_stats *ofs;
@@ -1072,7 +1135,38 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
orig_tun_table = fs->match.flow.tunnel.metadata.tab;
fs_->match.flow.tunnel.metadata.tab = tun_table;
- if (raw == OFPRAW_OFPST11_FLOW_REPLY || raw == OFPRAW_OFPST13_FLOW_REPLY) {
+ if (raw == OFPRAW_OFPST15_FLOW_REPLY) {
+ struct ofp15_flow_desc *ofd;
+
+ ofpbuf_put_uninit(reply, sizeof *ofd);
+ oxm_put_match(reply, &fs->match, version);
+
+ struct oxs_stats oxs = {
+ .duration_sec = fs->duration_sec,
+ .duration_nsec = fs->duration_nsec,
+ .idle_age = fs->idle_age >= 0 ? fs->idle_age : UINT32_MAX,
+ .packet_count = fs->packet_count,
+ .byte_count = fs->byte_count,
+ .flow_count = UINT32_MAX,
+ };
+ oxs_put_stats(reply, &oxs);
+
+ ofpacts_put_openflow_instructions(fs->ofpacts, fs->ofpacts_len, reply,
+ version);
+
+ ofd = ofpbuf_at_assert(reply, start_ofs, sizeof *ofd);
+ ofd->length = htons(reply->size - start_ofs);
+ ofd->table_id = fs->table_id;
+ ofd->priority = htons(fs->priority);
+ ofd->idle_timeout = htons(fs->idle_timeout);
+ ofd->hard_timeout = htons(fs->hard_timeout);
+ ofd->cookie = fs->cookie;
+ memset(ofd->pad2, 0, sizeof ofd->pad2);
+ ofd->pad = 0;
+ ofd->importance = htons(fs->importance);
+ ofd->flags = ofputil_encode_flow_mod_flags(fs->flags, version);
+ } else if (raw == OFPRAW_OFPST11_FLOW_REPLY ||
+ raw == OFPRAW_OFPST13_FLOW_REPLY) {
struct ofp11_flow_stats *ofs;
ofpbuf_put_uninit(reply, sizeof *ofs);
@@ -1160,6 +1254,18 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
fs_->match.flow.tunnel.metadata.tab = orig_tun_table;
}
+static void
+print_flow_stat(struct ds *string, const char *leader, uint64_t stat)
+{
+ ds_put_format(string, "%s%s=%s", colors.param, leader, colors.end);
+ if (stat != UINT64_MAX) {
+ ds_put_format(string, "%"PRIu64, stat);
+ } else {
+ ds_put_char(string, '?');
+ }
+ ds_put_cstr(string, ", ");
+}
+
/* Appends a textual form of 'fs' to 'string', translating port numbers to
* names using 'port_map' (if provided). If 'show_stats' is true, the output
* includes the flow duration, packet and byte counts, and its idle and hard
@@ -1188,10 +1294,8 @@ ofputil_flow_stats_format(struct ds *string,
ds_put_cstr(string, ", ");
}
if (show_stats) {
- ds_put_format(string, "%sn_packets=%s%"PRIu64", ",
- colors.param, colors.end, fs->packet_count);
- ds_put_format(string, "%sn_bytes=%s%"PRIu64", ",
- colors.param, colors.end, fs->byte_count);
+ print_flow_stat(string, "n_packets", fs->packet_count);
+ print_flow_stat(string, "n_bytes", fs->byte_count);
}
if (fs->idle_timeout != OFP_FLOW_PERMANENT) {
ds_put_format(string, "%sidle_timeout=%s%"PRIu16", ",
@@ -1248,20 +1352,33 @@ ofputil_encode_aggregate_stats_reply(
enum ofpraw raw;
ofpraw_decode(&raw, request);
- if (raw == OFPRAW_OFPST10_AGGREGATE_REQUEST) {
- packet_count = unknown_to_zero(stats->packet_count);
- byte_count = unknown_to_zero(stats->byte_count);
+ if (raw == OFPRAW_OFPST15_AGGREGATE_REQUEST) {
+ msg = ofpraw_alloc_stats_reply(request, 0);
+
+ struct oxs_stats oxs = {
+ .duration_sec = UINT32_MAX,
+ .duration_nsec = UINT32_MAX,
+ .idle_age = UINT32_MAX,
+ .packet_count = stats->packet_count,
+ .byte_count = stats->byte_count,
+ .flow_count = stats->flow_count,
+ };
+ oxs_put_stats(msg, &oxs);
} else {
- packet_count = stats->packet_count;
- byte_count = stats->byte_count;
- }
-
- msg = ofpraw_alloc_stats_reply(request, 0);
- asr = ofpbuf_put_zeros(msg, sizeof *asr);
- put_32aligned_be64(&asr->packet_count, htonll(packet_count));
- put_32aligned_be64(&asr->byte_count, htonll(byte_count));
- asr->flow_count = htonl(stats->flow_count);
+ if (raw == OFPRAW_OFPST10_AGGREGATE_REQUEST) {
+ packet_count = unknown_to_zero(stats->packet_count);
+ byte_count = unknown_to_zero(stats->byte_count);
+ } else {
+ packet_count = stats->packet_count;
+ byte_count = stats->byte_count;
+ }
+ msg = ofpraw_alloc_stats_reply(request, 0);
+ asr = ofpbuf_put_zeros(msg, sizeof *asr);
+ put_32aligned_be64(&asr->packet_count, htonll(packet_count));
+ put_32aligned_be64(&asr->byte_count, htonll(byte_count));
+ asr->flow_count = htonl(stats->flow_count);
+ }
return msg;
}
@@ -1270,12 +1387,27 @@ ofputil_decode_aggregate_stats_reply(struct ofputil_aggregate_stats *stats,
const struct ofp_header *reply)
{
struct ofpbuf msg = ofpbuf_const_initializer(reply, ntohs(reply->length));
- ofpraw_pull_assert(&msg);
+ enum ofpraw raw;
- struct ofp_aggregate_stats_reply *asr = msg.msg;
- stats->packet_count = ntohll(get_32aligned_be64(&asr->packet_count));
- stats->byte_count = ntohll(get_32aligned_be64(&asr->byte_count));
- stats->flow_count = ntohl(asr->flow_count);
+ raw = ofpraw_pull_assert(&msg);
+ if (raw == OFPRAW_OFPST15_AGGREGATE_REPLY) {
+ struct oxs_stats oxs;
+ uint16_t statlen;
+ uint8_t oxs_field_set;
+ enum ofperr error = oxs_pull_stat(&msg, &oxs, &statlen,
+ &oxs_field_set);
+ if (error) {
+ return error;
+ }
+ stats->packet_count = oxs.packet_count;
+ stats->byte_count = oxs.byte_count;
+ stats->flow_count = oxs.flow_count;
+ } else {
+ struct ofp_aggregate_stats_reply *asr = msg.msg;
+ stats->packet_count = ntohll(get_32aligned_be64(&asr->packet_count));
+ stats->byte_count = ntohll(get_32aligned_be64(&asr->byte_count));
+ stats->flow_count = ntohl(asr->flow_count);
+ }
return 0;
}
diff --git a/lib/ofp-monitor.c b/lib/ofp-monitor.c
index dd2a6bbeb..3d117cae8 100644
--- a/lib/ofp-monitor.c
+++ b/lib/ofp-monitor.c
@@ -29,6 +29,7 @@
#include "openvswitch/ofp-print.h"
#include "openvswitch/ofp-table.h"
#include "openvswitch/vlog.h"
+#include "ox-stat.h"
VLOG_DEFINE_THIS_MODULE(ofp_monitor);
@@ -71,7 +72,36 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
{
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
enum ofpraw raw = ofpraw_pull_assert(&b);
- if (raw == OFPRAW_OFPT11_FLOW_REMOVED) {
+ if (raw == OFPRAW_OFPT15_FLOW_REMOVED) {
+ const struct ofp15_flow_removed *ofr;
+ enum ofperr error;
+
+ ofr = ofpbuf_pull(&b, sizeof *ofr);
+
+ error = ofputil_pull_ofp11_match(&b, NULL, NULL, &fr->match, NULL);
+ if (error) {
+ return error;
+ }
+
+ struct oxs_stats stats;
+ uint16_t statlen;
+ uint8_t oxs_field_set;
+ error = oxs_pull_stat(&b, &stats, &statlen, &oxs_field_set);
+ if (error) {
+ return error;
+ }
+
+ fr->cookie = ofr->cookie;
+ fr->priority = ntohs(ofr->priority);
+ fr->reason = ofr->reason;
+ fr->table_id = ofr->table_id;
+ fr->duration_sec = stats.duration_sec;
+ fr->duration_nsec = stats.duration_nsec;
+ fr->idle_timeout = ntohs(ofr->idle_timeout);
+ fr->hard_timeout = ntohs(ofr->hard_timeout);
+ fr->packet_count = stats.packet_count;
+ fr->byte_count = stats.byte_count;
+ } else if (raw == OFPRAW_OFPT11_FLOW_REMOVED) {
const struct ofp12_flow_removed *ofr;
enum ofperr error;
@@ -167,9 +197,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
case OFPUTIL_P_OF11_STD:
case OFPUTIL_P_OF12_OXM:
case OFPUTIL_P_OF13_OXM:
- case OFPUTIL_P_OF14_OXM:
- case OFPUTIL_P_OF15_OXM:
- case OFPUTIL_P_OF16_OXM: {
+ case OFPUTIL_P_OF14_OXM: {
struct ofp12_flow_removed *ofr;
msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED,
@@ -190,7 +218,34 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
ofputil_put_ofp11_match(msg, &fr->match, protocol);
break;
}
+ case OFPUTIL_P_OF15_OXM:
+ case OFPUTIL_P_OF16_OXM: {
+ struct ofp15_flow_removed *ofr;
+
+ msg = ofpraw_alloc_xid(OFPRAW_OFPT15_FLOW_REMOVED,
+ ofputil_protocol_to_ofp_version(protocol),
+ htonl(0),
+ ofputil_match_typical_len(protocol));
+ ofr = ofpbuf_put_zeros(msg, sizeof *ofr);
+ ofr->cookie = fr->cookie;
+ ofr->priority = htons(fr->priority);
+ ofr->reason = reason;
+ ofr->table_id = fr->table_id;
+ ofr->idle_timeout = htons(fr->idle_timeout);
+ ofr->hard_timeout = htons(fr->hard_timeout);
+ ofputil_put_ofp11_match(msg, &fr->match, protocol);
+ const struct oxs_stats oxs = {
+ .duration_sec = fr->duration_sec,
+ .duration_nsec = fr->duration_nsec,
+ .idle_age = UINT32_MAX,
+ .packet_count = fr->packet_count,
+ .byte_count = fr->byte_count,
+ .flow_count = UINT32_MAX,
+ };
+ oxs_put_stats(msg, &oxs);
+ break;
+ }
case OFPUTIL_P_OF10_STD:
case OFPUTIL_P_OF10_STD_TID: {
struct ofp10_flow_removed *ofr;
diff --git a/lib/ox-stat.c b/lib/ox-stat.c
new file mode 100644
index 000000000..c6fbb4daa
--- /dev/null
+++ b/lib/ox-stat.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "ox-stat.h"
+#include "byte-order.h"
+#include "openvswitch/ofp-errors.h"
+#include "openvswitch/compiler.h"
+#include "openvswitch/ofpbuf.h"
+#include "openvswitch/vlog.h"
+#include "unaligned.h"
+
+VLOG_DEFINE_THIS_MODULE(ox_stat);
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+/* OXS header
+ * ==========
+ *
+ * The header is 32 bits long. It looks like this:
+ *
+ * |31 16 15 9 8 7 0
+ * +----------------------------------+---------------+-+------------------+
+ * | oxs_class | oxs_field |r| oxs_length |
+ * +----------------------------------+---------------+-+------------------+
+ *
+ * where r stands for oxs_reserved. It is followed by oxs_length bytes of
+ * payload (the statistic's value).
+ *
+ * Internally, we represent a standard OXS header as a 64-bit integer with the
+ * above information in the most-significant bits.
+ *
+ *
+ * Experimenter OXS
+ * ================
+ *
+ * The header is 64 bits long. It looks like the diagram above except that a
+ * 32-bit experimenter ID, which we call oxs_experimenter and which identifies
+ * a vendor, is inserted just before the payload. Experimenter OXSs are
+ * identified by an all-1-bits oxs_class (OFPXSC_EXPERIMENTER). The oxs_length
+ * value *includes* the experimenter ID, so that the real payload is only
+ * oxs_length - 4 bytes long.
+ *
+ * Internally, we represent an experimenter OXS header as a 64-bit integer with
+ * the standard header in the upper 32 bits and the experimenter ID in the
+ * lower 32 bits. (It would be more convenient to swap the positions of the
+ * two 32-bit words, but this would be more error-prone because experimenter
+ * OXSs are very rarely used, so accidentally passing one through a 32-bit type
+ * somewhere in the OVS code would be hard to find.)
+ */
+
+/* OXS Class IDs.
+ * The high order bit differentiate reserved classes from member classes.
+ * Classes 0x0000 to 0x7FFF are member classes, allocated by ONF.
+ * Classes 0x8000 to 0xFFFE are reserved classes, reserved for standardisation.
+ */
+enum ofp_oxs_class {
+ OFPXSC_OPENFLOW_BASIC = 0x8002, /* Basic stats class for OpenFlow */
+ OFPXSC_EXPERIMENTER = 0xFFFF, /* Experimenter class */
+};
+
+/* Functions for extracting raw field values from OXS headers. */
+static uint32_t oxs_experimenter(uint64_t header) { return header; }
+static int oxs_class(uint64_t header) { return header >> 48; }
+static int oxs_field(uint64_t header) { return (header >> 41) & 0x7f; }
+static int oxs_length(uint64_t header) { return (header >> 32) & 0xff; }
+
+static bool
+is_experimenter_oxs(uint64_t header)
+{
+ return oxs_class(header) == OFPXSC_EXPERIMENTER;
+}
+
+/* The OXS header "length" field is somewhat tricky:
+ *
+ * - For a standard OXS header, the length is the number of bytes of the
+ * payload, and the payload consists of just the value.
+ *
+ * - For an experimenter OXS header, the length is the number of bytes in
+ * the payload plus 4 (the length of the experimenter ID). That is, the
+ * experimenter ID is included in oxs_length.
+ *
+ * This function returns the length of the experimenter ID field in 'header'.
+ * That is, for an experimenter OXS (when an experimenter ID is present), it
+ * returns 4, and for a standard OXS (when no experimenter ID is present), it
+ * returns 0. */
+static int
+oxs_experimenter_len(uint64_t header)
+{
+ return is_experimenter_oxs(header) ? 4 : 0;
+}
+
+/* Returns the number of bytes that follow the header for an OXS entry with the
+ * given 'header'. */
+static int
+oxs_payload_len(uint64_t header)
+{
+ return oxs_length(header) - oxs_experimenter_len(header);
+}
+
+/* Returns the number of bytes in the header for an OXS entry with the given
+ * 'header'. */
+static int
+oxs_header_len(uint64_t header)
+{
+ return 4 + oxs_experimenter_len(header);
+}
+
+/* Assembles an OXS header from its components. */
+#define OXS_HEADER(EXPERIMENTER, CLASS, FIELD, LENGTH) \
+ (((uint64_t) (CLASS) << 48) | \
+ ((uint64_t) (FIELD) << 41) | \
+ ((uint64_t) (LENGTH) << 32) | \
+ (EXPERIMENTER))
+
+#define OXS_HEADER_FMT "%#"PRIx32":%d:%d:%d"
+#define OXS_HEADER_ARGS(HEADER) \
+ oxs_experimenter(HEADER), oxs_class(HEADER), oxs_field(HEADER), \
+ oxs_length(HEADER)
+
+/* Currently defined OXS. */
+#define OXS_OF_DURATION OXS_HEADER (0, 0x8002, OFPXST_OFB_DURATION, 8)
+#define OXS_OF_IDLE_TIME OXS_HEADER (0, 0x8002, OFPXST_OFB_IDLE_TIME, 8)
+#define OXS_OF_FLOW_COUNT OXS_HEADER (0, 0x8002, OFPXST_OFB_FLOW_COUNT, 4)
+#define OXS_OF_PACKET_COUNT OXS_HEADER (0, 0x8002, OFPXST_OFB_PACKET_COUNT, 8)
+#define OXS_OF_BYTE_COUNT OXS_HEADER (0, 0x8002, OFPXST_OFB_BYTE_COUNT, 8)
+
+/* Header for a group of OXS statistics. */
+struct ofp_oxs_stat {
+ ovs_be16 reserved; /* Must be zero. */
+ ovs_be16 length; /* Stats Length */
+};
+BUILD_ASSERT_DECL(sizeof(struct ofp_oxs_stat) == 4);
+
+static int oxs_pull_header__(struct ofpbuf *b, uint64_t *header);
+static enum ofperr oxs_pull_raw(const uint8_t *, unsigned int stat_len,
+ struct oxs_stats *, uint8_t *oxs_field_set);
+
+static int
+oxs_pull_header__(struct ofpbuf *b, uint64_t *header)
+{
+ if (b->size < 4) {
+ goto bad_len;
+ }
+
+ *header = ((uint64_t) ntohl(get_unaligned_be32(b->data))) << 32;
+ if (is_experimenter_oxs(*header)) {
+ if (b->size < 8) {
+ goto bad_len;
+ }
+ *header = ntohll(get_unaligned_be64(b->data));
+ }
+ if (oxs_length(*header) < oxs_experimenter_len(*header)) {
+ VLOG_WARN_RL(&rl, "OXS header "OXS_HEADER_FMT" has invalid length %d "
+ "(minimum is %d)",
+ OXS_HEADER_ARGS(*header), oxs_length(*header),
+ oxs_header_len(*header));
+ goto error;
+ }
+ ofpbuf_pull(b, oxs_header_len(*header));
+
+ return 0;
+
+bad_len:
+ VLOG_DBG_RL(&rl, "encountered partial (%"PRIu32"-byte) OXS entry",
+ b->size);
+error:
+ *header = 0;
+ return OFPERR_OFPBMC_BAD_LEN;
+}
+
+static enum ofperr
+oxs_pull_entry__(struct ofpbuf *b, struct oxs_stats *stats,
+ uint8_t *oxs_field_set)
+{
+ uint64_t header;
+ enum ofperr error = oxs_pull_header__(b, &header);
+ if (error) {
+ return error;
+ }
+
+ unsigned int payload_len = oxs_payload_len(header);
+ const void *payload = ofpbuf_try_pull(b, payload_len);
+ if (!payload) {
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
+
+ switch (header) {
+ case OXS_OF_DURATION: {
+ uint64_t duration = ntohll(get_unaligned_be64(payload));
+ stats->duration_sec = duration >> 32;
+ stats->duration_nsec = duration;
+ }
+ break;
+ case OXS_OF_IDLE_TIME:
+ stats->idle_age = ntohll(get_unaligned_be64(payload)) >> 32;
+ break;
+ case OXS_OF_PACKET_COUNT:
+ stats->packet_count = ntohll(get_unaligned_be64(payload));
+ break;
+ case OXS_OF_BYTE_COUNT:
+ stats->byte_count = ntohll(get_unaligned_be64(payload));
+ break;
+ case OXS_OF_FLOW_COUNT:
+ stats->flow_count = ntohl(get_unaligned_be32(payload));
+ break;
+
+ default:
+ /* Unknown header. */
+ return 0;
+ }
+ if (oxs_field_set
+ && oxs_class(header) == OFPXSC_OPENFLOW_BASIC
+ && oxs_field(header) < CHAR_BIT * sizeof *oxs_field_set) {
+ *oxs_field_set |= 1 << oxs_field(header);
+ }
+ return error;
+}
+
+static enum ofperr
+oxs_pull_raw(const uint8_t * p, unsigned int stat_len,
+ struct oxs_stats *stats, uint8_t *oxs_field_set)
+{
+ struct ofpbuf b = ofpbuf_const_initializer(p, stat_len);
+ while (b.size) {
+ const uint8_t *pos = b.data;
+ enum ofperr error = oxs_pull_entry__(&b, stats, oxs_field_set);
+ if (error && error != OFPERR_OFPBMC_BAD_FIELD) {
+ VLOG_DBG_RL(&rl, "error parsing OXS at offset %"PRIdPTR" "
+ "within match (%s)",
+ pos - p, ofperr_to_string(error));
+ return error;
+ }
+ }
+ return 0;
+}
+
+/* Retrieve struct ofp_oxs_stat from 'b', followed by the flow entry
+ * statistics in OXS format.
+ *
+ * Returns error if message parsing fails, otherwise returns zero . */
+enum ofperr
+oxs_pull_stat(struct ofpbuf *b, struct oxs_stats *stats,
+ uint16_t *statlen, uint8_t *oxs_field_set)
+{
+ memset(stats, 0xff, sizeof *stats);
+
+ struct ofp_oxs_stat *oxs = b->data;
+ if (b->size < sizeof *oxs) {
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
+
+ uint16_t stat_len = ntohs(oxs->length);
+ if (stat_len < sizeof *oxs) {
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
+
+ uint8_t *p = ofpbuf_try_pull(b, ROUND_UP(stat_len, 8));
+ if (!p) {
+ VLOG_DBG_RL(&rl, "oxs length %u, rounded up to a "
+ "multiple of 8, is longer than space in message (max "
+ "length %" PRIu32 ")", stat_len, b->size);
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
+ *statlen = ROUND_UP(stat_len, 8);
+ return oxs_pull_raw(p + sizeof *oxs, stat_len - sizeof *oxs, stats,
+ oxs_field_set);
+}
+
+static void
+oxs_put__(struct ofpbuf *b, uint64_t header,
+ const void *value, size_t value_size)
+{
+ if (is_experimenter_oxs(header)) {
+ ovs_be64 be64 = htonll(header);
+ ofpbuf_put(b, &be64, sizeof be64);
+ } else {
+ ovs_be32 be32 = htonl(header >> 32);
+ ofpbuf_put(b, &be32, sizeof be32);
+ }
+
+ ovs_assert(oxs_payload_len(header) == value_size);
+ ofpbuf_put(b, value, value_size);
+}
+
+static void
+oxs_put32(struct ofpbuf *b, uint64_t header, uint32_t value_)
+{
+ ovs_be32 value = htonl(value_);
+ oxs_put__(b, header, &value, sizeof value);
+}
+
+static void
+oxs_put64(struct ofpbuf *b, uint64_t header, uint64_t value_)
+{
+ ovs_be64 value = htonll(value_);
+ oxs_put__(b, header, &value, sizeof value);
+}
+
+/* Appends to 'b' an struct ofp_oxs_stat followed by the flow entry statistics
+ * in OXS format , plus enough zero bytes to pad the data appended out to a
+ * multiple of 8.
+ *
+ * Specify the OpenFlow version in use as 'version'.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns the number of bytes appended to 'b', excluding the padding.Never
+ * returns zero. */
+void
+oxs_put_stats(struct ofpbuf *b, const struct oxs_stats *stats)
+{
+ size_t start = b->size;
+
+ /* Put empty header. */
+ struct ofp_oxs_stat *oxs;
+ ofpbuf_put_zeros(b, sizeof *oxs);
+
+ /* Put stats. */
+ if (stats->duration_sec != UINT32_MAX) {
+ oxs_put64(b, OXS_OF_DURATION,
+ (((uint64_t) stats->duration_sec << 32)
+ | stats->duration_nsec));
+ }
+ if (stats->idle_age != UINT32_MAX) {
+ oxs_put64(b, OXS_OF_IDLE_TIME, (uint64_t) stats->idle_age << 32);
+ }
+ if (stats->packet_count != UINT64_MAX) {
+ oxs_put64(b, OXS_OF_PACKET_COUNT, stats->packet_count);
+ }
+ if (stats->byte_count != UINT64_MAX) {
+ oxs_put64(b, OXS_OF_BYTE_COUNT, stats->byte_count);
+ }
+ if (stats->flow_count != UINT32_MAX) {
+ oxs_put32(b, OXS_OF_FLOW_COUNT, stats->flow_count);
+ }
+
+ /* Fill in size in header, then pad to multiple of 8 bytes. */
+ oxs = ofpbuf_at(b, start, sizeof *oxs);
+ oxs->length = htons(b->size - start);
+ ofpbuf_put_zeros(b, PAD_SIZE(b->size - start, 8));
+}
diff --git a/lib/ox-stat.h b/lib/ox-stat.h
new file mode 100644
index 000000000..8eb104886
--- /dev/null
+++ b/lib/ox-stat.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2016 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OX_STAT_H
+#define OX_STAT_H 1
+
+#include <stdint.h>
+
+struct ofpbuf;
+
+struct oxs_stats {
+ uint32_t duration_sec;
+ uint32_t duration_nsec;
+ uint32_t idle_age;
+ uint64_t packet_count;
+ uint64_t byte_count;
+ uint32_t flow_count;
+};
+
+void oxs_put_stats(struct ofpbuf *, const struct oxs_stats *);
+enum ofperr oxs_pull_stat(struct ofpbuf *, struct oxs_stats *,
+ uint16_t *, uint8_t *);
+
+#endif /* ox_stat.h */
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index ab7ba26b0..cfce78002 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -638,6 +638,22 @@ OFPT_FLOW_REMOVED (OF1.3) (xid=0x0): dl_vlan=9 reason=hard table_id=5 cookie:0xf
])
AT_CLEANUP
+AT_SETUP([OFPT_FLOW_REMOVED - OF1.5])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+06 0b 00 80 00 00 00 02 01 00 00 00 11 00 22 00 \
+00 00 00 00 00 00 00 01 00 01 00 2d 80 00 00 04 \
+00 00 00 02 80 00 06 06 52 54 00 c3 00 89 80 00 \
+0a 02 08 00 80 00 10 01 00 80 00 04 08 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 34 80 02 00 08 \
+00 00 00 98 29 e6 ed c0 80 02 02 08 00 00 00 98 \
+00 00 00 00 80 02 08 08 00 00 00 00 00 00 00 02 \
+80 02 0a 08 00 00 00 00 00 00 00 80 00 00 00 00 \
+"], [0], [dnl
+OFPT_FLOW_REMOVED (OF1.5) (xid=0x2): priority=0,ip,metadata=0,in_port=2,dl_dst=52:54:00:c3:00:89,nw_tos=0 reason=idle table_id=1 cookie:0x1 duration152.703s idle4352 hard8704 pkts2 bytes128
+])
+AT_CLEANUP
+
AT_SETUP([OFPT_PORT_STATUS - OF1.0])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\
@@ -1392,6 +1408,18 @@ OFPST_FLOW request (OF1.3) (xid=0x2):
])
AT_CLEANUP
+AT_SETUP([OFPST_FLOW request - OF1.5])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+06 12 00 38 00 00 00 04 00 01 00 00 00 00 00 00 \
+ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 04 00 00 00 00 \
+"], [0], [dnl
+OFPST_FLOW request (OF1.5) (xid=0x4):
+])
+AT_CLEANUP
+
AT_SETUP([OFPST_FLOW reply - OF1.0])
AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
AT_CHECK([ovs-ofctl ofp-print "\
@@ -1471,6 +1499,32 @@ OFPST_FLOW reply (OF1.2) (xid=0x2):
])
AT_CLEANUP
+AT_SETUP([OFPST_FLOW reply - OF1.5])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+06 13 01 00 00 00 00 04 00 01 00 00 00 00 00 00 \
+00 78 00 00 00 00 80 00 00 00 00 00 00 05 00 00 \
+00 00 00 00 00 00 00 00 00 01 00 0c 80 00 00 04 \
+00 00 00 02 00 00 00 00 00 00 00 34 80 02 00 08 \
+00 00 00 c4 0b 06 e0 40 80 02 02 08 00 00 00 c4 \
+00 00 00 00 80 02 08 08 00 00 00 00 00 00 00 02 \
+80 02 0a 08 00 00 00 00 00 00 00 80 00 00 00 00 \
+00 04 00 18 00 00 00 00 00 00 00 10 ff ff ff fa \
+00 00 00 00 00 00 00 00 00 78 00 00 00 00 0f a0 \
+00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 0c 80 00 00 04 00 00 00 03 00 00 00 00 \
+00 00 00 34 80 02 00 08 00 00 00 b3 25 40 be 40 \
+80 02 02 08 00 00 00 b3 00 00 00 00 80 02 08 08 \
+00 00 00 00 00 00 00 02 80 02 0a 08 00 00 00 00 \
+00 00 00 80 00 00 00 00 00 04 00 18 00 00 00 00 \
+00 00 00 10 ff ff ff fa 00 00 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPST_FLOW reply (OF1.5) (xid=0x4):
+ cookie=0x0, duration=196.185s, table=0, n_packets=2, n_bytes=128, send_flow_rem reset_counts idle_age=196, in_port=2 actions=NORMAL
+ cookie=0x0, duration=179.625s, table=0, n_packets=2, n_bytes=128, send_flow_rem reset_counts idle_age=179, priority=4000,in_port=3 actions=NORMAL
+])
+AT_CLEANUP
+
AT_SETUP([OFPST_AGGREGATE request - OF1.0])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "\
@@ -1507,6 +1561,20 @@ OFPST_AGGREGATE request (OF1.3) (xid=0x2):
])
AT_CLEANUP
+AT_SETUP([OFPST_AGGREGATE request - OF1.5])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+06 12 00 60 00 00 00 04 00 02 00 00 00 00 00 00 \
+ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 04 00 00 00 00 00 00 00 24 80 02 06 04 \
+00 00 00 00 80 02 08 08 00 00 00 00 00 00 00 00 \
+80 02 0a 08 00 00 00 00 00 00 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPST_AGGREGATE request (OF1.5) (xid=0x4):
+])
+AT_CLEANUP
+
AT_SETUP([OFPST_AGGREGATE reply - OF1.0])
AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
AT_CHECK([ovs-ofctl ofp-print "\
@@ -1540,6 +1608,18 @@ OFPST_AGGREGATE reply (OF1.3) (xid=0x2): packet_count=121 byte_count=19279 flow_
])
AT_CLEANUP
+AT_SETUP([OFPST_AGGREGATE reply - OF1.5])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+06 13 00 38 00 00 00 04 00 02 00 00 00 00 00 00 \
+00 00 00 24 80 02 06 04 00 00 00 03 80 02 08 08 \
+00 00 00 00 00 00 00 79 80 02 0a 08 00 00 00 00 \
+00 00 4b 4f 00 00 00 00 \
+"], [0], [dnl
+OFPST_AGGREGATE reply (OF1.5) (xid=0x4): packet_count=121 byte_count=19279 flow_count=3
+])
+AT_CLEANUP
+
AT_SETUP([OFPST_TABLE request - OF1.0])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "0110000c0000000100030000"], [0], [dnl
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 60f28e2a0..4857eaf98 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -7290,11 +7290,96 @@ flow_mods_reset_counts () {
# OpenFlow versions >= 1.3 should behave the same way
flow_mods_reset_counts 13
flow_mods_reset_counts 14
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - flow stats reset_counts OpenFlow1.5])
+OVS_VSWITCHD_START
+flow="ip,actions=NORMAL"
+
+ovs-appctl time/stop
+
+AT_CHECK([ovs-ofctl add-flow br0 $flow])
+
+warp_and_dump_OF () {
+ AT_CHECK([ovs-appctl time/warp 1000], [0], [ignore])
+ AT_CHECK([ovs-appctl revalidator/purge], [0])
+
+ AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 dump-flows br0 ], [0], [stdout])
+ if [[ "$6X" = "X" ]]; then
+ expected=" cookie=0x0, duration=$2s, table=0, n_packets=$3, n_bytes=$4, idle_age=$5, ip actions=NORMAL"
+ else
+ expected=" cookie=0x0, duration=$2s, table=0, n_packets=$3, n_bytes=$4, $6 idle_age=$5, ip actions=NORMAL"
+ fi
+ AT_CHECK_UNQUOTED([strip_xids < stdout | sed -n 's/duration=\([[0-9]]*\)\.*[[0-9]]*s/duration=\1s/p' | sort], [0], [dnl
+$expected
+])
+}
+
+send_packet () {
+ ovs-appctl netdev-dummy/receive br0 'in_port(0),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no)'
+}
+
+# OpenFlow 1.5, explicit reset_counts
+flow_mods_reset_counts () {
+ # Reset to a known state
+ AT_CHECK([ovs-ofctl add-flow br0 $flow])
+
+ send_packet
+ warp_and_dump_OF $1 1 1 118 1 reset_counts
+ AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 $flow])
+ # add-flow without flags resets duration, but not counts,
+ # idle age is inherited from the old flow
+ warp_and_dump_OF $1 1 1 118 2
+
+ send_packet
+ warp_and_dump_OF $1 2 2 236 1
+ AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 $flow])
+ # mod-flows without flags does not reset duration nor counts,
+ # idle age is inherited from the old flow
+ warp_and_dump_OF $1 3 2 236 2
+
+ send_packet
+ warp_and_dump_OF $1 4 3 354 1
+ AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 reset_counts,$flow])
+ # add-flow with reset_counts resets both duration and counts,
+ # idle age is inherited from the old flow
+ warp_and_dump_OF $1 1 0 0 2 reset_counts
+
+ send_packet
+ warp_and_dump_OF $1 2 1 118 1 reset_counts
+ AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 reset_counts,$flow])
+ # mod-flows with reset_counts resets counts, but not duration,
+ # idle age is inherited from the old flow
+ warp_and_dump_OF $1 3 0 0 2 reset_counts
+
+ # Modify flow having reset_counts flag without reset_counts
+ send_packet
+ warp_and_dump_OF $1 4 1 118 1 reset_counts
+ AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 $flow])
+ warp_and_dump_OF $1 5 1 118 2 reset_counts
+
+ # Add flow having reset_counts flag without reset_counts
+ send_packet
+ warp_and_dump_OF $1 6 2 236 1 reset_counts
+ AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 $flow])
+ warp_and_dump_OF $1 1 2 236 2
+
+ # Modify flow w/o reset_counts flag with a flow_mod having reset_counts
+ send_packet
+ warp_and_dump_OF $1 2 3 354 1
+ AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 reset_counts,$flow])
+ warp_and_dump_OF $1 3 0 0 2
+}
+
+# OpenFlow versions >= 1.3 should behave the same way
flow_mods_reset_counts 15
OVS_VSWITCHD_STOP
AT_CLEANUP
+
AT_SETUP([ofproto-dpif - flow stats, set-n-threads])
OVS_VSWITCHD_START
AT_CHECK([ovs-ofctl add-flow br0 "ip,actions=NORMAL"])
diff --git a/tests/ofproto.at b/tests/ofproto.at
index bf4166aea..d8e9cc093 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -1402,6 +1402,7 @@ AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip], [0], [dnl
OFPST_FLOW reply (OF1.4):
check_overlap reset_counts in_port=1 actions=drop
])
+# OF1.5 makes the flags invisible.
AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flows br0 | ofctl_strip], [0], [dnl
OFPST_FLOW reply (OF1.5):
check_overlap reset_counts in_port=1 actions=drop