@@ -6,6 +6,13 @@ Post-v2.9.0
"table#". These are not helpful names for the purpose of accepting
and displaying table names, so now tables by default have no names.
- ovs-ofctl:
+ * Existing flow entry statistics are redefined as standard OXS(OpenFlow
+ Extensible Statistics) fields for displaying the arbitrary flow stats.
+ * The existing flow statistics are renamed as Flow Description.
+ * Now "ovs-ofctl dump-flows" and "ovs-ofctl dump-aggregate" accepts a new
+ option --oxs-stats provided with the arbitrary OXS fields i.e
+ flow duration, flow count, packet count, byte count for displaying
+ the desired flow stats. See ovs-ofctl(8) for details.
* ovs-ofctl now accepts and display table names in place of numbers. By
default it always accepts names and in interactive use it displays them;
use --names or --no-names to override. See ovs-ofctl(8) for details.
@@ -163,4 +163,85 @@ struct ofp15_packet_out {
};
OFP_ASSERT(sizeof(struct ofp15_packet_out) == 8);
+struct ofp_oxs_stat {
+ ovs_be16 reserved; /* Reserved for future use,
+ * currently zeroed. */
+ ovs_be16 length; /* Stats Length */
+};
+
+OFP_ASSERT(sizeof(struct ofp_oxs_stat) == 4);
+
+/* Body for ofp_multipart_request of type
+ * OFPMP_FLOW_DESC & OFPMP_FLOW_STATS. */
+struct ofp15_flow_stats_request {
+ uint8_t table_id; /* ID of table to read (from ofp_table_desc),
+ * OFPTT_ALL for all tables. */
+ uint8_t pad[3]; /* Align to 32 bits. */
+ ovs_be32 out_port; /* Require matching entries to include this as
+ * an output port. A value of OFP_ANY
+ * indicates no restriction. */
+ ovs_be32 out_group; /* Require matching entries to include this as
+ * an output group. A value of OFPG_ANY
+ * indicates no restriction. */
+ uint8_t pad2[4]; /* Align to 64 bits. */
+ ovs_be64 cookie; /* Require matching entries to contain this
+ * cookie value */
+ ovs_be64 cookie_mask; /* Mask used to restrict the cookie bits that
+ * must match. A value of 0 indicates no
+ * restriction. */
+};
+
+OFP_ASSERT(sizeof(struct ofp15_flow_stats_request) == 32);
+
+/* 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 */
@@ -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,
@@ -293,8 +295,12 @@ enum ofpraw {
/* OFPST 1.0 (1): struct ofp10_flow_stats_request. */
OFPRAW_OFPST10_FLOW_REQUEST,
- /* OFPST 1.1+ (1): struct ofp11_flow_stats_request, uint8_t[8][]. */
+ /* OFPST 1.1-1.4 (1): struct ofp11_flow_stats_request, uint8_t[8][]. */
OFPRAW_OFPST11_FLOW_REQUEST,
+ /* OFPST 1.5+ (1): struct ofp15_flow_stats_request, uint8_t[8][]. */
+ OFPRAW_OFPST15_FLOW_REQUEST,
+ /* OFPST 1.5+ (17): struct ofp15_flow_stats_request, uint8_t[8][]. */
+ OFPRAW_OFPST15_FLOW_DESC_REQUEST,
/* NXST 1.0 (0): struct nx_flow_stats_request, uint8_t[8][]. */
OFPRAW_NXST_FLOW_REQUEST,
@@ -302,20 +308,28 @@ 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,
+ /* OFPST 1.5+ (17): uint8_t[]. */
+ OFPRAW_OFPST15_FLOW_DESC_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 ofp15_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 +575,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.
@@ -637,16 +652,22 @@ enum ofptype {
OFPTYPE_DESC_STATS_REPLY, /* OFPRAW_OFPST_DESC_REPLY. */
OFPTYPE_FLOW_STATS_REQUEST, /* OFPRAW_OFPST10_FLOW_REQUEST.
* OFPRAW_OFPST11_FLOW_REQUEST.
+ * OFPRAW_OFPST15_FLOW_REQUEST.
* OFPRAW_NXST_FLOW_REQUEST. */
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_FLOW_DESC_REQUEST, /* OFPRAW_OFPST15_FLOW_DESC_REQUEST. */
+ OFPTYPE_FLOW_DESC_REPLY, /* OFPRAW_OFPST15_FLOW_DESC_REPLY. */
OFPTYPE_TABLE_STATS_REQUEST, /* OFPRAW_OFPST_TABLE_REQUEST. */
OFPTYPE_TABLE_STATS_REPLY, /* OFPRAW_OFPST10_TABLE_REPLY.
* OFPRAW_OFPST11_TABLE_REPLY.
@@ -34,6 +34,7 @@ struct ofputil_table_features;
struct ofputil_table_map;
struct ofputil_table_stats;
struct dp_packet;
+struct ofputil_aggregate_stats;
#ifdef __cplusplus
extern "C" {
@@ -62,12 +63,16 @@ void ofp_print_table_features(
const struct ofputil_table_stats *stats,
const struct ofputil_table_stats *prev_stats,
const struct ofputil_table_map *table_map);
-
void ofp_print_flow_stats(struct ds *, const struct ofputil_flow_stats *,
const struct ofputil_port_map *,
const struct ofputil_table_map *,
bool show_stats);
-
+void ofp_print_flow_oxs_stats(struct ds *string,
+ const struct ofputil_flow_stats *fs,
+ bool stats, uint8_t oxs_field_set);
+void ofp_print_flow_oxs_agg_stats(struct ds *,
+ const struct ofputil_aggregate_stats ,
+ bool , uint8_t );
#ifdef __cplusplus
}
#endif
@@ -216,6 +216,8 @@ lib_libopenvswitch_la_SOURCES = \
lib/ovsdb-parser.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 \
@@ -183,6 +183,7 @@ ofputil_is_bundlable(enum ofptype type)
case OFPTYPE_GET_ASYNC_REQUEST:
case OFPTYPE_DESC_STATS_REQUEST:
case OFPTYPE_FLOW_STATS_REQUEST:
+ case OFPTYPE_FLOW_DESC_REQUEST:
case OFPTYPE_AGGREGATE_STATS_REQUEST:
case OFPTYPE_TABLE_STATS_REQUEST:
case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
@@ -211,6 +212,7 @@ ofputil_is_bundlable(enum ofptype type)
case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
case OFPTYPE_DESC_STATS_REPLY:
case OFPTYPE_FLOW_STATS_REPLY:
+ case OFPTYPE_FLOW_DESC_REPLY:
case OFPTYPE_QUEUE_STATS_REPLY:
case OFPTYPE_PORT_STATS_REPLY:
case OFPTYPE_TABLE_STATS_REPLY:
@@ -30,6 +30,7 @@
#include "openvswitch/ofpbuf.h"
#include "openvswitch/vlog.h"
#include "util.h"
+#include "ox-stat.h"
VLOG_DEFINE_THIS_MODULE(ofp_flow);
@@ -552,6 +553,37 @@ ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
}
static enum ofperr
+ofputil_decode_ofpst15_flow_request(struct ofputil_flow_stats_request *fsr,
+ struct ofpbuf *b, bool aggregate,
+ const struct tun_table *tun_table,
+ const struct vl_mff_map *vl_mff_map)
+{
+ const struct ofp15_flow_stats_request *ofsr;
+ enum ofperr error;
+
+ ofsr = ofpbuf_pull(b, sizeof *ofsr);
+ fsr->aggregate = aggregate;
+ fsr->table_id = ofsr->table_id;
+
+ error = ofputil_port_from_ofp11(ofsr->out_port, &fsr->out_port);
+ if (error) {
+ return error;
+ }
+
+ fsr->out_group = ntohl(ofsr->out_group);
+ fsr->cookie = ofsr->cookie;
+ fsr->cookie_mask = ofsr->cookie_mask;
+
+ error = ofputil_pull_ofp11_match(b, tun_table, vl_mff_map, &fsr->match,
+ NULL);
+ if (error) {
+ return error;
+ }
+
+ return 0;
+}
+
+static enum ofperr
ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
struct ofpbuf *b, bool aggregate,
const struct tun_table *tun_table,
@@ -605,10 +637,18 @@ ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
return ofputil_decode_ofpst11_flow_request(fsr, &b, false, tun_table,
vl_mff_map);
+ case OFPRAW_OFPST15_FLOW_DESC_REQUEST:
+ return ofputil_decode_ofpst15_flow_request(fsr, &b, false,
+ tun_table, vl_mff_map);
+
case OFPRAW_OFPST11_AGGREGATE_REQUEST:
return ofputil_decode_ofpst11_flow_request(fsr, &b, true, tun_table,
vl_mff_map);
+ case OFPRAW_OFPST15_AGGREGATE_REQUEST:
+ return ofputil_decode_ofpst15_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);
@@ -637,9 +677,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
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 ofp11_flow_stats_request *ofsr;
raw = (fsr->aggregate
@@ -657,6 +695,25 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
break;
}
+ case OFPUTIL_P_OF15_OXM:
+ case OFPUTIL_P_OF16_OXM: {
+ struct ofp15_flow_stats_request *ofsr;
+
+ raw = (fsr->aggregate
+ ? OFPRAW_OFPST15_AGGREGATE_REQUEST
+ : OFPRAW_OFPST15_FLOW_DESC_REQUEST);
+ msg = ofpraw_alloc(raw, ofputil_protocol_to_ofp_version(protocol),
+ ofputil_match_typical_len(protocol));
+ ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
+ ofsr->table_id = fsr->table_id;
+ ofsr->out_port = ofputil_port_to_ofp11(fsr->out_port);
+ ofsr->out_group = htonl(fsr->out_group);
+ ofsr->cookie = fsr->cookie;
+ ofsr->cookie_mask = fsr->cookie_mask;
+ ofputil_put_ofp11_match(msg, &fsr->match, protocol);
+ break;
+ }
+
case OFPUTIL_P_OF10_STD:
case OFPUTIL_P_OF10_STD_TID: {
struct ofp10_flow_stats_request *ofsr;
@@ -773,6 +830,58 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
if (!msg->size) {
return EOF;
+ } else if (raw == OFPRAW_OFPST15_FLOW_DESC_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_DESC 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_DESC 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_DESC reply bad match");
+ return EINVAL;
+ }
+
+ fs->priority = ntohs(ofd->priority);
+ fs->table_id = ofd->table_id;
+ fs->duration_sec = 0;
+ fs->duration_nsec = 0;
+ fs->idle_age = 0;
+ fs->hard_age = -1;
+ fs->packet_count = 0;
+ fs->byte_count = 0;
+ 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;
+ }
+
+ if (oxs_pull_stat(msg, fs, &stat_len, &oxs_field_set)) {
+ VLOG_WARN_RL(&rl, "OXS OFPST_FLOW_DESC reply bad match");
+ return EINVAL;
+ }
+ 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;
@@ -951,7 +1060,31 @@ 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_DESC_REPLY) {
+ struct ofp15_flow_desc *ofd;
+ /* oxs_field_set is used to encode/decode OXS fields in TLV format*/
+ uint8_t oxs_field_set;
+
+ oxs_bitmap_set_all(&oxs_field_set);
+ ofpbuf_put_uninit(reply, sizeof *ofd);
+ oxm_put_match(reply, &fs->match, version);
+ oxs_put_stat(reply, fs, version, oxs_field_set);
+ 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);
@@ -1053,20 +1186,29 @@ 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) {
+ enum ofp_version version = request->version;
+ /* oxs_field_set is used to encode/decode OXS fields in TLV format*/
+ uint8_t oxs_set_field;
+
+ oxs_bitmap_set_all(&oxs_set_field);
+ msg = ofpraw_alloc_stats_reply(request, 0);
+ oxs_put_agg_stat(msg, stats, version, oxs_set_field);
} 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;
}
@@ -1075,12 +1217,22 @@ 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 ofperr error;
+ 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) {
+ memset(stats, 0, sizeof (*stats));
+ error = oxs_pull_agg_stat(msg, stats);
+ if (error) {
+ return error;
+ }
+ } 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;
}
@@ -1436,8 +1588,8 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
char *string = xstrdup(str_);
char *error;
- error = parse_ofp_str__(fm, command, string, port_map, table_map,
- usable_protocols);
+ error = parse_ofp_str__(fm, command, string, port_map,
+ table_map, usable_protocols);
if (error) {
fm->ofpacts = NULL;
fm->ofpacts_len = 0;
@@ -28,6 +28,7 @@
#include "openvswitch/ofp-parse.h"
#include "openvswitch/ofp-table.h"
#include "openvswitch/vlog.h"
+#include "ox-stat.h"
VLOG_DEFINE_THIS_MODULE(ofp_monitor);
@@ -42,7 +43,26 @@ 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;
+ }
+ oxs_flow_removed_stat_pull(&b,fr);
+
+ fr->priority = ntohs(ofr->priority);
+ fr->cookie = ofr->cookie;
+ fr->reason = ofr->reason;
+ fr->table_id = ofr->table_id;
+ fr->idle_timeout = ntohs(ofr->idle_timeout);
+ fr->hard_timeout = ntohs(ofr->hard_timeout);
+ } else if (raw == OFPRAW_OFPT11_FLOW_REMOVED) {
+
const struct ofp12_flow_removed *ofr;
enum ofperr error;
@@ -138,9 +158,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,
@@ -161,7 +179,27 @@ 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);
+ /*Stats encoding in OXS TLV Format*/
+ oxs_flow_removed_stat_put(msg, fr,
+ ofputil_protocol_to_ofp_version(protocol));
+ break;
+ }
case OFPUTIL_P_OF10_STD:
case OFPUTIL_P_OF10_STD_TID: {
struct ofp10_flow_removed *ofr;
@@ -27,6 +27,7 @@
#include "packets.h"
#include "socket-util.h"
#include "util.h"
+#include "ox-stat.h"
/* Parses 'str' as an 8-bit unsigned integer into '*valuep'.
*
@@ -63,6 +63,7 @@
#include "unaligned.h"
#include "util.h"
#include "uuid.h"
+#include "ox-stat.h"
static void ofp_print_queue_name(struct ds *string, uint32_t port);
static void ofp_print_error(struct ds *, enum ofperr);
@@ -1805,6 +1806,55 @@ ofp_print_flow_stats(struct ds *string, const struct ofputil_flow_stats *fs,
ofpacts_format(fs->ofpacts, fs->ofpacts_len, &fp);
}
+void
+ofp_print_flow_oxs_stats(struct ds *string,
+ const struct ofputil_flow_stats *fs,
+ bool oxs, uint8_t oxs_field_set)
+{
+ if (!oxs && oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_DURATION)) {
+ ds_put_format(string, "%sduration=%s", colors.param, colors.end);
+ ofp_print_duration(string, fs->duration_sec, fs->duration_nsec);
+ ds_put_cstr(string, " ");
+ }
+ if (!oxs) {
+ if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_PACKET_COUNT)) {
+ ds_put_format(string, "%sn_packets=%s%"PRIu64" ",
+ colors.param, colors.end, fs->packet_count);
+ }
+ if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_BYTE_COUNT)) {
+ ds_put_format(string, "%sn_bytes=%s%"PRIu64" ",
+ colors.param, colors.end, fs->byte_count);
+ }
+ }
+ if (!oxs && fs->idle_age >= 0 &&
+ oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_IDLE_TIME)) {
+ ds_put_format(string, "%sidle_age=%s%d ",
+ colors.param, colors.end, fs->idle_age);
+ }
+
+}
+
+void
+ofp_print_flow_oxs_agg_stats(struct ds *string,
+ const struct ofputil_aggregate_stats fs,
+ bool stats, uint8_t oxs_field_set)
+{
+ if (!stats) {
+ if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_PACKET_COUNT)) {
+ ds_put_format(string, "%sn_packets=%s%"PRIu64" ",
+ colors.param, colors.end, fs.packet_count);
+ }
+ if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_BYTE_COUNT)) {
+ ds_put_format(string, "%sn_bytes=%s%"PRIu64" ",
+ colors.param, colors.end, fs.byte_count);
+ }
+ if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_FLOW_COUNT)) {
+ ds_put_format(string, "%sflow_count=%s%"PRIu32" ",
+ colors.param, colors.end, fs.flow_count);
+ }
+ }
+}
+
static enum ofperr
ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh,
const struct ofputil_port_map *port_map,
@@ -3684,6 +3734,7 @@ ofp_to_string__(const struct ofp_header *oh,
case OFPTYPE_FLOW_STATS_REQUEST:
case OFPTYPE_AGGREGATE_STATS_REQUEST:
+ case OFPTYPE_FLOW_DESC_REQUEST:
ofp_print_stats(string, oh);
return ofp_print_flow_stats_request(string, oh, port_map, table_map);
@@ -3704,6 +3755,7 @@ ofp_to_string__(const struct ofp_header *oh,
return ofp_print_ofpst_desc_reply(string, oh);
case OFPTYPE_FLOW_STATS_REPLY:
+ case OFPTYPE_FLOW_DESC_REPLY:
ofp_print_stats(string, oh);
return ofp_print_flow_stats_reply(string, oh, port_map, table_map);
new file mode 100644
@@ -0,0 +1,1040 @@
+/*
+ * 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 "nx-match.h"
+#include <netinet/icmp6.h>
+#include "classifier.h"
+#include "colors.h"
+#include "openvswitch/hmap.h"
+#include "openflow/nicira-ext.h"
+#include "openvswitch/dynamic-string.h"
+#include "openvswitch/meta-flow.h"
+#include "openvswitch/ofp-actions.h"
+#include "openvswitch/ofp-errors.h"
+#include "openvswitch/ofp-util.h"
+#include "openvswitch/ofpbuf.h"
+#include "openvswitch/vlog.h"
+#include "packets.h"
+#include "openvswitch/shash.h"
+#include "tun-metadata.h"
+#include "unaligned.h"
+#include "util.h"
+#include "ox-stat.h"
+
+VLOG_DEFINE_THIS_MODULE(ox_stat);
+
+/* ## -------------------------- ## */
+/* ## OpenFlow Extensible Stats. ## */
+/* ## -------------------------- ## */
+
+/* Components of a OXS TLV header. */
+
+static struct ovs_list oxs_ox_map[OFPXST_OFB_BYTE_COUNT + 1];
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+static uint32_t
+oxs_header_no_len(uint32_t header)
+{
+ return header & 0xffffff80;
+}
+
+#define OXS_CLASS(HEADER) ((HEADER) >> 16)
+#define OXS_FIELD(HEADER) (((HEADER) >> 9) & 0x7f)
+#define OXS_TYPE(HEADER) (((HEADER) >> 9) & 0x7fffff)
+#define OXS_RESERVED(HEADER) (((HEADER) >> 8) & 1)
+#define OXS_LENGTH(HEADER) ((HEADER) & 0xff)
+
+/* Components of a OXS TLV header. */
+#define OXS_HEADER__(CLASS, FIELD, RESERVED, LENGTH) \
+(((CLASS) << 16) | ((FIELD) << 9) | ((RESERVED) << 8) | (LENGTH))
+
+#define OXS_HEADER(CLASS, FIELD, LENGTH) \
+ OXS_HEADER__(CLASS, FIELD, 0, LENGTH)
+
+/* 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 */
+};
+
+#define OFPXST_OFB_ALL ((UINT64_C(1) << 6) - 1)
+#define OXS_OX_COOKIE OXS_HEADER (0x8002, 5 , 8)
+
+struct oxs_field {
+ uint32_t header;
+ enum ofp_version version;
+ const char *name;
+ enum oxs_ofb_stat_fields id;
+};
+
+struct oxs_field_index {
+ struct hmap_node header_node;
+ struct hmap_node name_node;
+ struct ovs_list ox_node;
+ const struct oxs_field fs;
+};
+
+#define OXS_STATS_DURATION_LEN 8
+#define OXS_STATS_IDLE_TIME_LEN 8
+#define OXS_STATS_FLOW_COUNT_LEN 4
+#define OXS_STATS_PACKET_COUNT_LEN 8
+#define OXS_STATS_BYTE_COUNT_LEN 8
+
+#define OXS_OF_DURATION OXS_HEADER (0x8002, OFPXST_OFB_DURATION, \
+ OXS_STATS_DURATION_LEN)
+#define OXS_OF_IDLE_TIME OXS_HEADER (0x8002, OFPXST_OFB_IDLE_TIME, \
+ OXS_STATS_IDLE_TIME_LEN)
+#define OXS_OF_FLOW_COUNT OXS_HEADER (0x8002, OFPXST_OFB_FLOW_COUNT, \
+ OXS_STATS_FLOW_COUNT_LEN)
+#define OXS_OF_PACKET_COUNT OXS_HEADER (0x8002, OFPXST_OFB_PACKET_COUNT, \
+ OXS_STATS_PACKET_COUNT_LEN)
+#define OXS_OF_BYTE_COUNT OXS_HEADER (0x8002, OFPXST_OFB_BYTE_COUNT, \
+ OXS_STATS_BYTE_COUNT_LEN)
+
+static struct oxs_field_index all_oxs_fields[] = {
+ {.fs = {OXS_OF_DURATION, OFP15_VERSION, "OFPXST_OFB_DURATION",
+ OFPXST_OFB_DURATION}},
+ {.fs = {OXS_OF_IDLE_TIME, OFP15_VERSION, "OFPXST_OFB_IDLE_TIME",
+ OFPXST_OFB_IDLE_TIME}},
+ {.fs = {OXS_OF_FLOW_COUNT, OFP15_VERSION, "OFPXST_OFB_FLOW_COUNT",
+ OFPXST_OFB_FLOW_COUNT}},
+ {.fs = {OXS_OF_PACKET_COUNT, OFP15_VERSION, "OFPXST_OFB_PACKET_COUNT",
+ OFPXST_OFB_PACKET_COUNT}},
+ {.fs = {OXS_OF_BYTE_COUNT, OFP15_VERSION, "OFPXST_OFB_BYTE_COUNT",
+ OFPXST_OFB_BYTE_COUNT}},
+};
+
+static const struct oxs_field *oxs_field_by_header(uint32_t header);
+static const struct oxs_field *oxs_field_by_id(enum oxs_ofb_stat_fields,
+ enum ofp_version);
+static bool is_experimenter_oxs(uint64_t header);
+static int oxs_experimenter_len(uint64_t header);
+static int oxs_payload_len (uint64_t header);
+static int oxs_header_len (uint64_t header);
+static uint64_t oxs_header_get(enum oxs_ofb_stat_fields id,
+ enum ofp_version version);
+static int oxs_pull_header__(struct ofpbuf *b, uint64_t * header,
+ const struct oxs_field **field);
+static enum ofperr oxs_pull_entry__(struct ofpbuf *b, uint64_t * header,
+ const struct oxs_field **field_,
+ struct ofputil_flow_stats *fs);
+static enum ofperr oxs_pull_match_entry(struct ofpbuf *b,
+ const struct oxs_field **field,
+ struct ofputil_flow_stats *fs);
+static enum ofperr oxs_pull_raw(const uint8_t *, unsigned int ,
+ struct ofputil_flow_stats *fs,
+ ovs_be64 * cookie, ovs_be64 * cookie_mask,
+ uint8_t *oxs_field_set);
+static enum ofperr oxs_pull_agg_raw(const uint8_t * p, unsigned int stat_len,
+ struct ofputil_aggregate_stats *fs);
+static int oxs_flow_rem_stat_fields_pull(const uint8_t *p,
+ unsigned int stat_len,
+ struct ofputil_flow_removed *fr);
+static int oxs_pull_flow_rem_stat_entry(struct ofpbuf *b,
+ struct ofputil_flow_removed *fr);
+static void oxs_init(void);
+static void oxs_put_header__(struct ofpbuf *b, uint64_t header);
+static void oxs_put_header_len(struct ofpbuf *b,
+ enum oxs_ofb_stat_fields field,
+ enum ofp_version version);
+static int ox_put_agg_raw(struct ofpbuf *b, enum ofp_version oxs,
+ const struct ofputil_aggregate_stats *fs,
+ uint8_t oxs_field_set);
+static void oxs_put_duration(struct ofpbuf *b,
+ const struct ofputil_flow_removed *fr,
+ enum ofp_version version);
+static void oxs_put_packet_count(struct ofpbuf *b,
+ const struct ofputil_flow_removed *fr,
+ enum ofp_version version);
+static void oxs_put_byte_count(struct ofpbuf *b,
+ const struct ofputil_flow_removed *fr,
+ enum ofp_version version);
+static int oxs_flow_rem_stat_fields_put(struct ofpbuf *b,
+ const struct ofputil_flow_removed *fr,
+ enum ofp_version version);
+
+bool
+parse_oxs_key(char **stringp, char **keyp)
+{
+ /* Skip white space and delimiters. If that brings us to the end of the
+ * input string, we are done. */
+ *stringp += strspn(*stringp, ", \t\r\n");
+ if (*(*stringp) == '\0') {
+ *keyp = NULL;
+ return false;
+ }
+
+ /* Extract the key and the delimiter that ends the key.
+ * Advance the input position past the key and delimiter. */
+ char *key = *stringp;
+ size_t key_len = strcspn(key, ":=(, \t\r\n");
+ char key_delim = key[key_len];
+ key[key_len] = '\0';
+ *stringp += key_len + (key_delim != '\0');
+
+ *keyp = key;
+ return true;
+}
+
+bool
+parse_oxs_field(const char *name, const struct ox_field **f_out)
+{
+ static const struct ox_field fields[] = {
+ {"duration", OFPXST_OFB_DURATION},
+ {"idle_time", OFPXST_OFB_IDLE_TIME},
+ {"flow_count", OFPXST_OFB_FLOW_COUNT},
+ {"packet_count", OFPXST_OFB_PACKET_COUNT},
+ {"byte_count", OFPXST_OFB_BYTE_COUNT},
+ };
+
+ const struct ox_field *oxfs;
+
+ for (oxfs = fields; oxfs < &fields[ARRAY_SIZE(fields)]; oxfs++) {
+ if (!strcmp(oxfs->name, name)) {
+ *f_out = oxfs;
+ return true;
+ }
+ }
+ *f_out = NULL;
+ return false;
+}
+
+void
+oxs_bitmap_set_all(uint8_t *oxs_field_set)
+{
+ enum oxs_ofb_stat_fields i;
+
+ for (i = OFPXST_OFB_DURATION; i <= OFPXST_OFB_BYTE_COUNT; i++) {
+ *oxs_field_set |= 1 << i;
+ }
+}
+
+void
+oxs_bitmap_set(uint8_t *oxs_field_set, enum oxs_ofb_stat_fields i)
+{
+ *oxs_field_set |= 1 << i;
+
+}
+
+bool
+oxs_bitmap_is_set(uint8_t oxs_field_set, enum oxs_ofb_stat_fields i)
+{
+ if (oxs_field_set & 1 << i) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static bool
+is_experimenter_oxs(uint64_t header)
+{
+ return OXS_CLASS(header) == OFPXSC_EXPERIMENTER;
+}
+
+static int
+oxs_experimenter_len(uint64_t header)
+{
+ return is_experimenter_oxs(header) ? 4 : 0;
+}
+
+static int
+oxs_payload_len(uint64_t header)
+{
+ return OXS_LENGTH(header) - oxs_experimenter_len(header);
+}
+
+static int
+oxs_header_len(uint64_t header)
+{
+ return 4 + oxs_experimenter_len(header);
+}
+
+static uint64_t
+oxs_header_get(enum oxs_ofb_stat_fields id, enum ofp_version version)
+{
+ const struct oxs_field *f = oxs_field_by_id(id, version);
+
+ return f ? f->header : 0;
+}
+
+static int
+oxs_pull_header__(struct ofpbuf *b, uint64_t * header,
+ const struct oxs_field **field)
+{
+ if (b->size < 4) {
+ goto bad_len;
+ }
+
+ *header = ((uint32_t) ntohl(get_unaligned_be32(b->data)));
+ 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)) {
+ goto error;
+ }
+
+ ofpbuf_pull(b, oxs_header_len(*header));
+
+ if (field) {
+ *field = oxs_field_by_header(*header);
+ if (!*field || (*field == NULL)) {
+ return OFPERR_OFPBMC_BAD_FIELD;
+ }
+ }
+ return 0;
+
+bad_len:
+ VLOG_DBG_RL(&rl, "encountered partial (%" PRIu32 "-byte) OXS entry",
+ b->size);
+error:
+ *header = 0;
+ if (field) {
+ *field = NULL;
+ }
+ return OFPERR_OFPBMC_BAD_LEN;
+}
+
+static enum ofperr
+oxs_pull_entry__(struct ofpbuf *b, uint64_t * header,
+ const struct oxs_field **field_,
+ struct ofputil_flow_stats *fs)
+{
+ const struct oxs_field *field;
+ enum ofperr header_error;
+ unsigned int payload_len;
+ const uint8_t *payload;
+
+ header_error = oxs_pull_header__(b, header, &field);
+
+ if (header_error && header_error != OFPERR_OFPBMC_BAD_FIELD) {
+ return header_error;
+ }
+
+ payload_len = oxs_payload_len(*header);
+ payload = ofpbuf_try_pull(b, payload_len);
+ if (!payload) {
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
+ if (fs && field) {
+ switch (field->id) {
+ case OFPXST_OFB_DURATION: {
+ ovs_be64 duration = 0;
+
+ memcpy(&duration, payload, sizeof (duration));
+ uint64_t duration_ = ntohll(duration);
+ fs->duration_sec = ((uint32_t) ((duration_ &
+ 0xFFFFFFFF00000000LL) >> 32));
+ fs->duration_nsec = ((uint32_t) (duration_ & 0xFFFFFFFFLL));
+ }
+ break;
+ case OFPXST_OFB_IDLE_TIME: {
+ ovs_be64 idle_time = 0;
+
+ memcpy(&idle_time, payload, sizeof (idle_time));
+ uint64_t idle_time_ = ntohll(idle_time);
+ fs->idle_age = ((idle_time_ & 0xFFFFFFFF00000000LL) >> 32);
+ }
+ break;
+ case OFPXST_OFB_PACKET_COUNT: {
+ ovs_be64 packet_count;
+
+ memcpy(&packet_count, payload, sizeof (packet_count));
+ fs->packet_count = ntohll(packet_count);
+ }
+ break;
+ case OFPXST_OFB_BYTE_COUNT: {
+ ovs_be64 byte_count;
+
+ memcpy(&byte_count, payload, sizeof (byte_count));
+ fs->byte_count = ntohll(byte_count);
+ }
+ break;
+ case OFPXST_OFB_FLOW_COUNT:
+ break;
+ }
+ }
+
+ if (field_) {
+ *field_ = field;
+ return header_error;
+ }
+
+ return 0;
+}
+
+static enum ofperr
+oxs_pull_match_entry(struct ofpbuf *b,
+ const struct oxs_field **field,
+ struct ofputil_flow_stats *fs)
+{
+ enum ofperr error;
+ uint64_t header;
+
+ error = oxs_pull_entry__(b, &header, field, fs);
+ if (error) {
+ return error;
+ }
+ return 0;
+}
+
+static enum ofperr
+oxs_pull_raw(const uint8_t * p, unsigned int stat_len,
+ struct ofputil_flow_stats *fs,
+ ovs_be64 * cookie, ovs_be64 * cookie_mask, uint8_t *oxs_field_set)
+{
+ ovs_assert((cookie != NULL) == (cookie_mask != NULL));
+ if (cookie) {
+ *cookie = *cookie_mask = htonll(0);
+ }
+
+ struct ofpbuf b = ofpbuf_const_initializer(p, stat_len);
+
+ while (b.size) {
+ const uint8_t *pos = b.data;
+ const struct oxs_field *field;
+ union mf_value value;
+ union mf_value mask;
+ enum ofperr error;
+
+ error = oxs_pull_match_entry(&b, &field, fs);
+ if (error) {
+ if (error == OFPERR_OFPBMC_BAD_FIELD && !false) {
+ continue;
+ }
+ } else if (!field) {
+ if (!cookie) {
+ error = OFPERR_OFPBMC_BAD_FIELD;
+ } else if (*cookie_mask) {
+ error = OFPERR_OFPBMC_DUP_FIELD;
+ } else {
+ *cookie = value.be64;
+ *cookie_mask = mask.be64;
+ }
+ } else {
+ oxs_bitmap_set(oxs_field_set,field->id);
+ }
+ if (error) {
+ 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 . */
+int
+oxs_pull_stat(struct ofpbuf *b, struct ofputil_flow_stats *fs,
+ uint16_t * statlen, uint8_t *oxs_field_set)
+{
+ struct ofp_oxs_stat *oxs = b->data;
+ uint8_t *p;
+ uint16_t stat_len;
+ stat_len = ntohs(oxs->length);
+ if (stat_len < sizeof *oxs) {
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
+
+ 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, fs, NULL,
+ NULL, oxs_field_set);
+}
+
+static enum ofperr
+oxs_pull_agg_raw(const uint8_t * p, unsigned int stat_len,
+ struct ofputil_aggregate_stats *fs)
+{
+ struct ofpbuf b = ofpbuf_const_initializer(p, stat_len);
+
+ while (b.size) {
+
+ uint64_t header;
+ unsigned int payload_len;
+ const struct oxs_field *field;
+ const uint8_t *payload;
+
+ oxs_pull_header__(&b, &header, &field);
+ payload_len = oxs_payload_len(header);
+ payload = ofpbuf_try_pull(&b, payload_len);
+
+ if (fs && field) {
+ switch (field->id) {
+ case OFPXST_OFB_FLOW_COUNT: {
+ ovs_be32 flow_count;
+
+ memcpy(&flow_count, payload, sizeof (flow_count));
+ fs->flow_count = ntohl(flow_count);
+ }
+ break;
+ case OFPXST_OFB_PACKET_COUNT: {
+ ovs_be64 packet_count;
+
+ memcpy(&packet_count, payload, sizeof (packet_count));
+ fs->packet_count = ntohll(packet_count);
+ }
+ break;
+ case OFPXST_OFB_BYTE_COUNT: {
+ ovs_be64 byte_count;
+
+ memcpy(&byte_count, payload, sizeof (byte_count));
+ fs->byte_count = ntohll(byte_count);
+ }
+ break;
+ case OFPXST_OFB_DURATION:
+ case OFPXST_OFB_IDLE_TIME:
+ break;
+ }
+
+ }
+
+ }
+ return 0;
+}
+
+/* Retrieve struct ofp_oxs_stat from 'b', followed by the aggregate flow entry
+ * statistics in OXS format.
+ *
+ * Returns error if message parsing fails, otherwise returns zero . */
+int
+oxs_pull_agg_stat(struct ofpbuf b, struct ofputil_aggregate_stats *fs)
+{
+ struct ofp_oxs_stat *oxs = b.data;
+ uint8_t *p;
+ uint16_t stat_len;
+
+ stat_len = ntohs(oxs->length);
+
+ if (stat_len < sizeof *oxs) {
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
+
+ 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;
+ }
+
+ return oxs_pull_agg_raw(p + sizeof *oxs, stat_len - sizeof *oxs, fs);
+}
+
+static struct hmap oxs_header_map;
+static struct hmap oxs_name_map;
+
+static void
+oxs_init(void)
+{
+ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+
+ if (ovsthread_once_start(&once)) {
+ hmap_init(&oxs_header_map);
+ hmap_init(&oxs_name_map);
+ for (int i = 0; i < OFPXST_OFB_BYTE_COUNT + 1; i++) {
+ ovs_list_init(&oxs_ox_map[i]);
+ }
+ for (struct oxs_field_index * oxfs = all_oxs_fields;
+ oxfs < &all_oxs_fields[ARRAY_SIZE(all_oxs_fields)]; oxfs++) {
+ hmap_insert(&oxs_header_map, &oxfs->header_node,
+ hash_int(oxs_header_no_len(oxfs->fs.header), 0));
+ hmap_insert(&oxs_name_map, &oxfs->name_node,
+ hash_string(oxfs->fs.name, 0));
+ ovs_list_push_back(&oxs_ox_map[oxfs->fs.id], &oxfs->ox_node);
+ }
+ ovsthread_once_done(&once);
+ }
+}
+
+static const struct oxs_field *
+oxs_field_by_header(uint32_t header)
+{
+ const struct oxs_field_index *oxfs;
+ uint32_t header_no_len;
+
+ oxs_init();
+
+ header_no_len = oxs_header_no_len(header);
+ HMAP_FOR_EACH_IN_BUCKET (oxfs, header_node, hash_int(header_no_len, 0),
+ &oxs_header_map) {
+ if (header_no_len == oxs_header_no_len(oxfs->fs.header)) {
+ if (OXS_LENGTH(header) == OXS_LENGTH(oxfs->fs.header)) {
+ return &oxfs->fs;
+ } else {
+ return NULL;
+ }
+ }
+ }
+ return NULL;
+}
+
+static const struct oxs_field *
+oxs_field_by_id(enum oxs_ofb_stat_fields id, enum ofp_version version)
+{
+ const struct oxs_field_index *oxfs;
+ const struct oxs_field *fs = NULL;
+
+ oxs_init();
+
+ LIST_FOR_EACH (oxfs, ox_node, &oxs_ox_map[id]) {
+ if (!fs || version >= oxfs->fs.version) {
+ fs = &oxfs->fs;
+ }
+ }
+ return fs;
+}
+
+static void
+oxs_put_header__(struct ofpbuf *b, uint64_t header)
+{
+ ovs_be32 network_header = htonl(header);
+
+ ofpbuf_put(b, &network_header, oxs_header_len(header));
+}
+
+static void
+oxs_put_header_len(struct ofpbuf *b, enum oxs_ofb_stat_fields field,
+ enum ofp_version version)
+{
+ uint32_t header = oxs_header_get(field, version);
+ header = OXS_HEADER(OXS_CLASS(header),
+ OXS_FIELD(header), OXS_LENGTH(header));
+ oxs_put_header__(b, header);
+}
+
+void
+oxs_put__(struct ofpbuf *b, enum oxs_ofb_stat_fields field,
+ enum ofp_version version,
+ const void *value, const void *mask, size_t n_bytes)
+{
+ oxs_put_header_len(b, field, version);
+ ofpbuf_put(b, value, n_bytes);
+ if (mask) {
+ ofpbuf_put(b, mask, n_bytes);
+ }
+
+}
+
+/* Appends to 'b' the flow entry statistics in a flexible encoding ,OXS
+ * format.
+ *
+ * OXS is a TLV format to express flow entry statistics. Specify the OpenFlow
+ * version in use as 'version'.
+ *
+ * Returns the number of bytes appended to 'b'.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns zero or the number of bytes appended to 'b'. */
+static int
+ox_put_raw(struct ofpbuf *b, enum ofp_version oxs,
+ const struct ofputil_flow_stats *fs,
+ ovs_be64 cookie, ovs_be64 cookie_mask, uint8_t oxs_field_set)
+{
+ const size_t start_len = b->size;
+ int stat_len;
+
+ if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_DURATION)) {
+ ovs_be64 duration = 0;
+
+ if (fs) {
+ uint64_t duration_ = (uint64_t) fs->duration_sec << 32
+ | fs->duration_nsec;
+ duration = htonll(duration_);
+ }
+ oxs_put__(b, OFPXST_OFB_DURATION, oxs, &duration, NULL,
+ OXS_STATS_DURATION_LEN);
+ }
+ if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_IDLE_TIME)) {
+ ovs_be64 idl_time = 0;
+
+ if (fs) {
+ uint64_t idl_time_n = (uint64_t) fs->idle_age << 32;
+ idl_time = htonll(idl_time_n);
+ }
+ oxs_put__(b, OFPXST_OFB_IDLE_TIME, oxs, &idl_time, NULL,
+ OXS_STATS_IDLE_TIME_LEN);
+ }
+ if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_FLOW_COUNT)) {
+ ovs_be32 flow_count = 0;
+
+ oxs_put__(b, OFPXST_OFB_FLOW_COUNT, oxs, &flow_count, NULL,
+ OXS_STATS_FLOW_COUNT_LEN);
+ }
+ if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_PACKET_COUNT)) {
+ ovs_be64 pkt_count = 0;
+
+ if (fs) {
+ pkt_count = htonll(fs->packet_count);
+ }
+ oxs_put__(b, OFPXST_OFB_PACKET_COUNT, oxs, &pkt_count, NULL,
+ OXS_STATS_PACKET_COUNT_LEN);
+ }
+ if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_BYTE_COUNT)) {
+ ovs_be64 byte_count = 0;
+
+ if (fs) {
+ byte_count = htonll(fs->byte_count);
+ }
+ oxs_put__(b, OFPXST_OFB_BYTE_COUNT, oxs, &byte_count, NULL,
+ OXS_STATS_BYTE_COUNT_LEN);
+ }
+ if (cookie_mask) {
+ cookie &= cookie_mask;
+ oxs_put_header__(b, OXS_OX_COOKIE);
+ ofpbuf_put(b, &cookie, sizeof cookie);
+ }
+ stat_len = b->size - start_len;
+ return stat_len;
+}
+
+/* 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. */
+int
+oxs_put_stat(struct ofpbuf *b, const struct ofputil_flow_stats *fs,
+ enum ofp_version version, uint8_t oxs_field_set)
+{
+ int stat_len;
+ struct ofp_oxs_stat *oxs;
+ size_t start_len = b->size;
+ ovs_be64 cookie = htonll(0), cookie_mask = htonll(0);
+
+ ofpbuf_put_uninit(b, sizeof *oxs);
+ stat_len = (ox_put_raw(b, version, fs, cookie, cookie_mask, oxs_field_set)
+ + sizeof *oxs);
+ ofpbuf_put_zeros(b, PAD_SIZE(stat_len, 8));
+ oxs = ofpbuf_at(b, start_len, sizeof *oxs);
+ oxs->reserved = htons(0);
+ oxs->length = htons(stat_len);
+ return stat_len;
+}
+
+/* Appends to 'b' the aggregate flow entry statistics in a flexible encoding,
+ * OXS format.
+ *
+ * OXS is a TLV format to express flow entry statistics. Specify the OpenFlow
+ * version in use as 'version'.
+ *
+ * Returns the number of bytes appended to 'b'.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns zero or the number of bytes appended to 'b'. */
+static int
+ox_put_agg_raw(struct ofpbuf *b, enum ofp_version oxs,
+ const struct ofputil_aggregate_stats *fs, uint8_t oxs_field_set)
+{
+ const size_t start_len = b->size;
+ int stat_len;
+
+ if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_FLOW_COUNT)) {
+ ovs_be32 flow_count = 0;
+
+ if (fs) {
+ flow_count = htonl(fs->flow_count);
+ }
+ oxs_put__(b, OFPXST_OFB_FLOW_COUNT, oxs, &flow_count,
+ NULL, OXS_STATS_FLOW_COUNT_LEN);
+ }
+
+ if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_PACKET_COUNT)) {
+ ovs_be64 pkt_count = 0;
+
+ if (fs) {
+ pkt_count = htonll(fs->packet_count);
+ }
+ oxs_put__(b, OFPXST_OFB_PACKET_COUNT, oxs, &pkt_count,
+ NULL, OXS_STATS_PACKET_COUNT_LEN);
+ }
+
+ if (oxs_bitmap_is_set(oxs_field_set, OFPXST_OFB_BYTE_COUNT)) {
+ ovs_be64 byte_count = 0;
+
+ if (fs) {
+ byte_count = htonll(fs->byte_count);
+ }
+ oxs_put__(b, OFPXST_OFB_BYTE_COUNT, oxs, &byte_count,
+ NULL, OXS_STATS_BYTE_COUNT_LEN);
+ }
+
+ stat_len = b->size - start_len;
+ return stat_len;
+}
+
+/* Appends to 'b' an struct ofp_oxs_stat followed by the aggregate 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. */
+int
+oxs_put_agg_stat(struct ofpbuf *b, const struct ofputil_aggregate_stats *fs,
+ enum ofp_version version, uint8_t oxs_set_field)
+{
+ int stat_len;
+ struct ofp_oxs_stat *oxs;
+ size_t start_len = b->size;
+
+ ofpbuf_put_uninit(b, sizeof *oxs);
+ stat_len = (ox_put_agg_raw(b, version, fs, oxs_set_field)
+ + sizeof *oxs);
+ ofpbuf_put_zeros(b, PAD_SIZE(stat_len, 8));
+ oxs = ofpbuf_at(b, start_len, sizeof *oxs);
+ oxs->reserved = htons(0);
+ oxs->length = htons(stat_len);
+
+ return stat_len;
+}
+
+static void
+oxs_put_duration(struct ofpbuf *b,
+ const struct ofputil_flow_removed *fr,
+ enum ofp_version version)
+{
+ ovs_be64 duration = 0;
+
+ if (fr) {
+ uint64_t duration_ = (uint64_t) fr->duration_sec << 32
+ | fr->duration_nsec;
+ duration = htonll(duration_);
+ }
+ oxs_put__(b, OFPXST_OFB_DURATION, version, &duration, NULL,
+ OXS_STATS_DURATION_LEN);
+ return;
+}
+
+static void
+oxs_put_packet_count(struct ofpbuf *b,
+ const struct ofputil_flow_removed *fr,
+ enum ofp_version version)
+{
+ ovs_be64 pkt_count = 0;
+
+ if (fr) {
+ pkt_count = htonll(fr->packet_count);
+ }
+ oxs_put__(b, OFPXST_OFB_PACKET_COUNT, version, &pkt_count, NULL,
+ OXS_STATS_PACKET_COUNT_LEN);
+ return;
+}
+
+static void
+oxs_put_byte_count(struct ofpbuf *b,
+ const struct ofputil_flow_removed *fr,
+ enum ofp_version version)
+{
+ ovs_be64 byte_count = 0;
+
+ if (fr) {
+ byte_count = htonll(fr->byte_count);
+ }
+ oxs_put__(b, OFPXST_OFB_BYTE_COUNT, version, &byte_count, NULL,
+ OXS_STATS_BYTE_COUNT_LEN);
+ return;
+}
+
+static int
+oxs_flow_rem_stat_fields_put(struct ofpbuf *b,
+ const struct ofputil_flow_removed *fr,
+ enum ofp_version version)
+{
+ int stat_len;
+ size_t start_len = b->size;
+
+ oxs_put_duration(b, fr, version);
+ oxs_put_packet_count(b, fr, version);
+ oxs_put_byte_count(b, fr, version);
+
+ stat_len = (b->size - start_len);
+ return stat_len;
+}
+
+/* Encode Flow entry statistics in FLOW_REMOVED message
+ *
+ * Appends to 'b' the flow entry statistics in a flexible encoding ,OXS
+ * format.
+ *
+ * OXS is a TLV format to express flow entry statistics. Specify the OpenFlow
+ * version in use as 'version'.
+ *
+ * Returns the number of bytes appended to 'b'.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns zero or the number of bytes appended to 'b'. */
+int
+oxs_flow_removed_stat_put(struct ofpbuf *b,
+ const struct ofputil_flow_removed *fr,
+ enum ofp_version version)
+{
+ int stat_len;
+ struct ofp_oxs_stat *oxs;
+ size_t start_len = b->size;
+
+ ofpbuf_put_uninit(b, sizeof *oxs);
+ stat_len = oxs_flow_rem_stat_fields_put(b, fr, version) + sizeof *oxs;
+ ofpbuf_put_zeros(b, PAD_SIZE(stat_len, 8));
+ oxs = ofpbuf_at(b, start_len, sizeof *oxs);
+ oxs->reserved = htons(0);
+ oxs->length = htons(stat_len);
+ return stat_len;
+}
+
+static int
+oxs_flow_rem_stat_fields_pull(const uint8_t * p, unsigned int stat_len,
+ struct ofputil_flow_removed *fr)
+{
+ struct ofpbuf b = ofpbuf_const_initializer(p, stat_len);
+
+ while (b.size) {
+
+ const uint8_t *pos = b.data;
+ enum ofperr error;
+
+ error = oxs_pull_flow_rem_stat_entry(&b, fr);
+ if (error) {
+ if (error == OFPERR_OFPBMC_BAD_FIELD && !false) {
+ continue;
+ }
+ }
+ if (error) {
+ VLOG_DBG_RL(&rl, "error parsing OXS at offset %" PRIdPTR " "
+ "within match (%s)", pos - p, ofperr_to_string(error));
+ return error;
+ }
+ }
+ return 0;
+}
+
+static int
+oxs_pull_flow_rem_stat_entry(struct ofpbuf *b, struct ofputil_flow_removed *fr)
+{
+ const struct oxs_field *field;
+ uint64_t header;
+ enum ofperr header_error;
+ unsigned int payload_len;
+ const uint8_t *payload;
+
+ header_error = oxs_pull_header__(b, &header, &field);
+ if (header_error && header_error != OFPERR_OFPBMC_BAD_FIELD) {
+ return header_error;
+ }
+
+ payload_len = oxs_payload_len(header);
+ payload = ofpbuf_try_pull(b, payload_len);
+ if (!payload) {
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
+ if (fr && field) {
+
+ switch (field->id) {
+ case OFPXST_OFB_DURATION: {
+ ovs_be64 duration;
+
+ memcpy(&duration, payload, sizeof (duration));
+ uint64_t duration_ = ntohll(duration);
+ fr->duration_sec = ((uint32_t) ((duration_ &
+ 0xFFFFFFFF00000000LL) >> 32));
+ fr->duration_nsec = ((uint32_t) (duration_ & 0xFFFFFFFFLL));
+ }
+ break;
+ case OFPXST_OFB_PACKET_COUNT: {
+ ovs_be64 packet_count;
+
+ memcpy(&packet_count, payload, sizeof (packet_count));
+ fr->packet_count = ntohll(packet_count);
+
+ }
+ break;
+ case OFPXST_OFB_BYTE_COUNT: {
+ ovs_be64 byte_count;
+
+ memcpy(&byte_count, payload, sizeof (byte_count));
+ fr->byte_count = ntohll(byte_count);
+ }
+ break;
+ case OFPXST_OFB_IDLE_TIME:
+ case OFPXST_OFB_FLOW_COUNT:
+ default:
+ break;
+
+ }
+ }
+ 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 . */
+int
+oxs_flow_removed_stat_pull(struct ofpbuf *b,
+ struct ofputil_flow_removed *ofr) {
+ struct ofp_oxs_stat *oxs = b->data;
+ uint8_t *p;
+ uint16_t stat_len;
+
+ stat_len = ntohs(oxs->length);
+ if (stat_len < sizeof *oxs) {
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
+
+ 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;
+ }
+
+ return oxs_flow_rem_stat_fields_pull(p + sizeof (*oxs),
+ stat_len - sizeof (*oxs), ofr);
+
+}
new file mode 100644
@@ -0,0 +1,60 @@
+/*
+ * 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>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include "compiler.h"
+#include "flow.h"
+#include "openvswitch/meta-flow.h"
+#include "openvswitch/ofp-errors.h"
+#include "openvswitch/types.h"
+#include "openvswitch/ofp-flow.h"
+#include "openvswitch/ofp-monitor.h"
+
+struct ox_field {
+ const char *name;
+ uint16_t fl_type;
+};
+
+int oxs_put_stat(struct ofpbuf *, const struct ofputil_flow_stats *,
+ enum ofp_version, uint8_t );
+int oxs_pull_stat(struct ofpbuf *,struct ofputil_flow_stats *,
+ uint16_t *,uint8_t *);
+int oxs_put_agg_stat(struct ofpbuf *, const struct ofputil_aggregate_stats *,
+ enum ofp_version, uint8_t );
+int oxs_pull_agg_stat(struct ofpbuf , struct ofputil_aggregate_stats *);
+int oxs_flow_removed_stat_put(struct ofpbuf *b,
+ const struct ofputil_flow_removed *fr,
+ enum ofp_version version);
+int oxs_flow_removed_stat_pull(struct ofpbuf *b,
+ struct ofputil_flow_removed *ofr);
+void
+oxs_bitmap_set(uint8_t *oxs_field_set, enum oxs_ofb_stat_fields i);
+void
+oxs_bitmap_set_all(uint8_t *oxs_field_set);
+bool
+oxs_bitmap_is_set(uint8_t oxs_field_set, enum oxs_ofb_stat_fields i);
+void oxs_put__(struct ofpbuf *, enum oxs_ofb_stat_fields,
+ enum ofp_version, const void *, const void *, size_t);
+bool parse_oxs_key(char **stringp, char **keyp);
+bool parse_oxs_field(const char *name, const struct ox_field **f_out);
+
+
+#endif /* ox_stat.h */
@@ -1401,6 +1401,8 @@ is_admitted_msg(const struct ofpbuf *b)
case OFPTYPE_DESC_STATS_REPLY:
case OFPTYPE_FLOW_STATS_REQUEST:
case OFPTYPE_FLOW_STATS_REPLY:
+ case OFPTYPE_FLOW_DESC_REQUEST:
+ case OFPTYPE_FLOW_DESC_REPLY:
case OFPTYPE_AGGREGATE_STATS_REQUEST:
case OFPTYPE_AGGREGATE_STATS_REPLY:
case OFPTYPE_TABLE_STATS_REQUEST:
@@ -940,7 +940,6 @@ recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid,
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
struct ofpbuf *reply = *replyp;
-
for (;;) {
int retval;
bool more;
@@ -958,14 +957,14 @@ recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid,
} while (((struct ofp_header *) reply->data)->xid != send_xid);
error = ofptype_decode(&type, reply->data);
- if (error || type != OFPTYPE_FLOW_STATS_REPLY) {
+ if (error || (type != OFPTYPE_FLOW_DESC_REPLY &&
+ type != OFPTYPE_FLOW_STATS_REPLY)) {
VLOG_WARN_RL(&rl, "received bad reply: %s",
ofp_to_string(reply->data, reply->size,
NULL, NULL, 1));
return EPROTO;
}
}
-
/* Pull an individual flow stats reply out of the message. */
retval = ofputil_decode_flow_stats_reply(fs, reply, false, ofpacts);
switch (retval) {
@@ -1004,7 +1003,6 @@ vconn_dump_flows(struct vconn *vconn,
struct ofputil_flow_stats *fses = NULL;
size_t n_fses = 0;
size_t allocated_fses = 0;
-
struct ofpbuf *request = ofputil_encode_flow_stats_request(fsr, protocol);
const struct ofp_header *oh = request->data;
ovs_be32 send_xid = oh->xid;
@@ -1023,6 +1021,7 @@ vconn_dump_flows(struct vconn *vconn,
struct ofputil_flow_stats *fs = &fses[n_fses];
error = recv_flow_stats_reply(vconn, send_xid, &reply, fs, &ofpacts);
+
if (error) {
if (error == EOF) {
error = 0;
@@ -4485,7 +4485,6 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
if (error) {
return error;
}
-
memset(&stats, 0, sizeof stats);
unknown_packets = unknown_bytes = false;
@@ -8116,6 +8115,7 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
return handle_desc_stats_request(ofconn, oh);
case OFPTYPE_FLOW_STATS_REQUEST:
+ case OFPTYPE_FLOW_DESC_REQUEST:
return handle_flow_stats_request(ofconn, oh);
case OFPTYPE_AGGREGATE_STATS_REQUEST:
@@ -8193,6 +8193,7 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
case OFPTYPE_DESC_STATS_REPLY:
case OFPTYPE_FLOW_STATS_REPLY:
+ case OFPTYPE_FLOW_DESC_REPLY:
case OFPTYPE_QUEUE_STATS_REPLY:
case OFPTYPE_PORT_STATS_REPLY:
case OFPTYPE_TABLE_STATS_REPLY:
@@ -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 11 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_DESC 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 11 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_DESC 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
@@ -7265,11 +7265,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"])
@@ -1402,8 +1402,9 @@ 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):
+OFPST_FLOW_DESC reply (OF1.5):
check_overlap reset_counts in_port=1 actions=drop
])
OVS_VSWITCHD_STOP
@@ -227,8 +227,8 @@ By default, \fBovs\-ofctl\fR prints flow entries in the same order
that the switch sends them, which is unlikely to be intuitive or
consistent. Use \fB\-\-sort\fR and \fB\-\-rsort\fR to control display
order. The \fB\-\-names\fR/\fB\-\-no\-names\fR and
-\fB\-\-stats\fR/\fB\-\-no\-stats\fR options also affect output
-formatting. See the descriptions of these options, under
+\fB\-\-stats\fR/\fB\-\-no\-stats\fR \fB\-\-oxs\-stats\fR options also affect
+output formatting. See the descriptions of these options, under
\fBOPTIONS\fR below, for more information
.
.TP
@@ -2358,6 +2358,33 @@ Bundles require OpenFlow 1.4 or higher. An explicit \fB-O
OpenFlow14\fR option is not needed, but you may need to enable
OpenFlow 1.4 support for OVS by setting the OVSDB \fIprotocols\fR
column in the \fIbridge\fR table.
+.
+.RE
+.RE
+.IP "\fB\-\-oxs-stats\fR"
+From OpenFlow1.5, the flow stat fields are described using the OpenFlow
+Extensible Stat (OXS) format, which is a compact type-length-value (TLV)
+format. Some \fBovs\-ofctl\fR commands accept an argument that describes oxs-
+field or oxs-fields. Such oxs-fields comprise a series of fields,
+separated by commas or white space. (Embedding spaces into a flow
+description normally requires quoting to prevent the shell from breaking
+the description into multiple arguments.)
+.PP
+The oxs-fields comprises of 5 fields and oxs-field value is only
+displayed in the flow table output only when the corresponding oxs-field
+is passed as an argument.
+.RS
+.IP \fBduration\fR
+Displays the entire duration of the flow in the output.
+.IP \fBidle_time\fR
+Displays the time that flow entry has been idle in the output.
+.IP \fBpacket_count\fR
+Displays the total packet-count of the flow in the output.
+.IP \fBbyte_count\fR
+Displays the total packet-count of the flow in the output.
+.IP \fBflow_count\fR
+Displays the total flow count in the output.
+.RE
.RE
.
.so lib/ofp-version.man
@@ -75,6 +75,7 @@
#include "timeval.h"
#include "unixctl.h"
#include "util.h"
+#include "ox-stat.h"
VLOG_DEFINE_THIS_MODULE(ofctl);
@@ -150,6 +151,10 @@ static bool should_show_names(void);
/* --stats, --no-stats: Show statistics in flow dumps? */
static int show_stats = 1;
+/* --oxs-stats: Show only requested OXS-fields in the flow dump */
+static bool oxs = 1;
+static uint8_t oxs_field_set = 0;
+
/* --pcap: Makes "compose-packet" print a pcap on stdout. */
static int print_pcap = 0;
@@ -182,6 +187,25 @@ main(int argc, char *argv[])
}
static void
+set_oxs_bitmap(char *field)
+{
+
+ char *keyp;
+ char *fieldp;
+ fieldp = field;
+
+ while (parse_oxs_key(&fieldp, &keyp)) {
+ const struct ox_field *oxfs;
+
+ if (parse_oxs_field(keyp, &oxfs)) {
+ oxs_bitmap_set(&oxs_field_set,oxfs->fl_type);
+ } else {
+ ovs_fatal(0, "%s:unknown oxs field", keyp);
+ }
+ }
+}
+
+static void
add_sort_criterion(enum sort_order order, const char *field)
{
struct sort_criterion *sc;
@@ -215,6 +239,7 @@ parse_options(int argc, char *argv[])
OPT_BUNDLE,
OPT_COLOR,
OPT_MAY_CREATE,
+ OPT_OXS_STATS,
OPT_READ_ONLY,
DAEMON_OPTION_ENUMS,
OFP_VERSION_OPTION_ENUMS,
@@ -235,6 +260,7 @@ parse_options(int argc, char *argv[])
{"no-names", no_argument, &use_names, 0},
{"stats", no_argument, &show_stats, 1},
{"no-stats", no_argument, &show_stats, 0},
+ {"oxs-stats", required_argument, NULL, OPT_OXS_STATS},
{"unixctl", required_argument, NULL, OPT_UNIXCTL},
{"help", no_argument, NULL, 'h'},
{"option", no_argument, NULL, 'o'},
@@ -377,6 +403,11 @@ parse_options(int argc, char *argv[])
may_create = true;
break;
+ case OPT_OXS_STATS:
+ oxs = 0;
+ set_oxs_bitmap(optarg);
+ break;
+
DAEMON_OPTION_HANDLERS
OFP_VERSION_OPTION_HANDLERS
VLOG_OPTION_HANDLERS
@@ -425,7 +456,20 @@ parse_options(int argc, char *argv[])
allowed_protocols &= version_protocols;
mask_allowed_ofp_versions(ofputil_protocols_to_version_bitmap(
allowed_protocols));
-
+ /* Openflow 1.5 should also be given explicitly with --oxs-stats option */
+ if (!oxs) {
+ enum ofputil_protocol oxs_allowed_protocols = OFPUTIL_P_OF15_UP;
+ if (!(oxs_allowed_protocols & version_protocols)) {
+ char *protocols =
+ ofputil_protocols_to_string(oxs_allowed_protocols);
+ struct ds version_s = DS_EMPTY_INITIALIZER;
+
+ ofputil_format_version_bitmap_names(&version_s, versions);
+ ovs_fatal(0, "None of the enabled OpenFlow versions (%s) supports "
+ "oxs (%s).(Use -O to enable additional OpenFlow versions)",
+ ds_cstr(&version_s), protocols);
+ }
+ }
colors_init(enable_color);
}
@@ -693,6 +737,50 @@ dump_transaction(struct vconn *vconn, struct ofpbuf *request)
}
static void
+dump_agg_transaction(struct vconn *vconn, struct ofpbuf *request,
+ struct ofputil_aggregate_stats *stats)
+{
+ const struct ofp_header *oh = request->data;
+ if (ofpmsg_is_stat_request(oh)) {
+ ovs_be32 send_xid = oh->xid;
+ enum ofpraw request_raw;
+ bool done = false;
+
+ ofpraw_decode_partial(&request_raw, request->data, request->size);
+ ofpraw_stats_request_to_reply(request_raw, oh->version);
+ send_openflow_buffer(vconn, request);
+
+ while (!done) {
+ ovs_be32 recv_xid;
+ struct ofpbuf *reply;
+
+ run(vconn_recv_block(vconn, &reply),
+ "OpenFlow packet receive failed");
+ recv_xid = ((struct ofp_header *) reply->data)->xid;
+ if (send_xid == recv_xid) {
+
+ ofputil_decode_aggregate_stats_reply(stats, reply->data);
+ done = true;
+ } else {
+ VLOG_DBG("received reply with xid %08"PRIx32" "
+ "!= expected %08"PRIx32, recv_xid, send_xid);
+ }
+ ofpbuf_delete(reply);
+ }
+ } else {
+ struct ofpbuf *reply;
+
+ run(vconn_transact(vconn, request, &reply), "talking to %s",
+ vconn_get_name(vconn));
+ ofp_print(stdout, reply->data, reply->size,
+ ports_to_show(vconn_get_name(vconn)),
+ tables_to_show(vconn_get_name(vconn)),
+ verbosity + 1);
+ ofpbuf_delete(reply);
+ }
+}
+
+static void
dump_trivial_transaction(const char *vconn_name, enum ofpraw raw)
{
struct ofpbuf *request;
@@ -1475,7 +1563,6 @@ ofctl_dump_flows__(int argc, char *argv[], bool aggregate)
struct ofputil_flow_stats_request fsr;
enum ofputil_protocol protocol;
struct vconn *vconn;
-
vconn = prepare_dump_flows(argc, argv, aggregate, &fsr, &protocol);
dump_transaction(vconn, ofputil_encode_flow_stats_request(&fsr, protocol));
vconn_close(vconn);
@@ -1550,7 +1637,7 @@ compare_flows(const void *afs_, const void *bfs_)
static void
ofctl_dump_flows(struct ovs_cmdl_context *ctx)
{
- if (!n_criteria && !should_show_names() && show_stats) {
+ if (!n_criteria && !should_show_names() && show_stats && oxs) {
ofctl_dump_flows__(ctx->argc, ctx->argv, false);
return;
} else {
@@ -1571,8 +1658,12 @@ ofctl_dump_flows(struct ovs_cmdl_context *ctx)
struct ds s = DS_EMPTY_INITIALIZER;
for (size_t i = 0; i < n_fses; i++) {
ds_clear(&s);
- ofp_print_flow_stats(&s, &fses[i], ports_to_show(ctx->argv[1]),
- tables_to_show(ctx->argv[1]), show_stats);
+ if (!oxs) {
+ ofp_print_flow_oxs_stats(&s, &fses[i], oxs, oxs_field_set);
+ } else {
+ ofp_print_flow_stats(&s, &fses[i], ports_to_show(ctx->argv[1]),
+ tables_to_show(ctx->argv[1]), show_stats);
+ }
printf(" %s\n", ds_cstr(&s));
}
ds_destroy(&s);
@@ -1589,7 +1680,27 @@ ofctl_dump_flows(struct ovs_cmdl_context *ctx)
static void
ofctl_dump_aggregate(struct ovs_cmdl_context *ctx)
{
- ofctl_dump_flows__(ctx->argc, ctx->argv, true);
+ if (oxs) {
+ ofctl_dump_flows__(ctx->argc, ctx->argv, true);
+ } else {
+ struct ofputil_aggregate_stats stats;
+ struct ofputil_flow_stats_request fsr;
+ enum ofputil_protocol protocol;
+ struct vconn *vconn;
+
+ vconn = prepare_dump_flows(ctx->argc, ctx->argv, true,
+ &fsr, &protocol);
+ dump_agg_transaction(vconn,
+ ofputil_encode_flow_stats_request(&fsr,
+ protocol), &stats);
+ struct ds s = DS_EMPTY_INITIALIZER;
+ ds_clear(&s);
+ ofp_print_flow_oxs_agg_stats(&s, stats, oxs, oxs_field_set);
+ printf(" %s\n", ds_cstr(&s));
+ ds_destroy(&s);
+
+ vconn_close(vconn);
+ }
}
static void