@@ -50,9 +50,12 @@
#include "unaligned.h"
#include "util.h"
#include "uuid.h"
+#include "ox-stat.h"
VLOG_DEFINE_THIS_MODULE(ofp_util);
+extern uint8_t oxs_field_set;
+
/* Rate limit for OpenFlow message parse errors. These always indicate a bug
* in the peer and so there's not much point in showing a lot of them. */
static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@ -2314,6 +2317,40 @@ 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_oxs_flow_stats_request *ofsr;
+ enum ofperr error, stat_error;
+ uint16_t statlen;
+
+ 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);
+ stat_error = oxs_pull_stat(b, NULL, &statlen);
+
+ if (error || stat_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,
@@ -2763,6 +2800,11 @@ 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_OXS_FLOW_REQUEST:
+ oxs_field_set = 0;
+ return ofputil_decode_ofpst15_flow_request(fsr, &b, false, tun_table,
+ vl_mff_map);
+
case OFPRAW_NXST_FLOW_REQUEST:
return ofputil_decode_nxst_flow_request(fsr, &b, false, tun_table,
vl_mff_map);
@@ -2788,12 +2830,29 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
enum ofpraw raw;
switch (protocol) {
+ case OFPUTIL_P_OF15_OXM:
+ case OFPUTIL_P_OF16_OXM: {
+ struct ofp15_oxs_flow_stats_request *ofsr;
+
+ raw = (fsr->aggregate
+ ? OFPRAW_OFPST11_AGGREGATE_REQUEST
+ : OFPRAW_OFPST15_OXS_FLOW_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);
+ oxs_put_stat(msg, NULL, ofputil_protocol_to_ofp_version(protocol));
+ break;
+ }
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
@@ -2893,6 +2952,46 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
if (!msg->size) {
return EOF;
+ } else if (raw == OFPRAW_OFPST15_OXS_FLOW_REPLY) {
+ const struct ofp15_oxs_flow_stats_reply *ofs;
+ size_t length;
+ uint16_t padded_match_len;
+ uint16_t stat_len;
+
+ ofs = ofpbuf_try_pull(msg, sizeof *ofs);
+ if (!ofs) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply has %" PRIu32
+ " leftover " "bytes at end", msg->size);
+ return EINVAL;
+ }
+
+ length = ntohs(ofs->length);
+ if (length < sizeof *ofs) {
+ VLOG_WARN_RL(&bad_ofmsg_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(&bad_ofmsg_rl, "OFPST_FLOW reply bad match");
+ return EINVAL;
+ }
+
+ fs->priority = ntohs(ofs->priority);
+ fs->table_id = ofs->table_id;
+ fs->duration_sec = 0;
+ fs->duration_nsec = 0;
+ fs->idle_age = 0;
+ fs->packet_count = 0;
+ fs->byte_count = 0;
+ fs->cookie = 0;
+
+ if (oxs_pull_stat(msg, fs, &stat_len)) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OXS OFPST_FLOW reply bad match");
+ return EINVAL;
+ }
+ instructions_len = length - sizeof *ofs - padded_match_len - stat_len;
} else if (raw == OFPRAW_OFPST11_FLOW_REPLY
|| raw == OFPRAW_OFPST13_FLOW_REPLY) {
const struct ofp11_flow_stats *ofs;
@@ -3067,7 +3166,23 @@ 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_OXS_FLOW_REPLY) {
+ struct ofp15_oxs_flow_stats_reply *ofs;
+
+ ofpbuf_put_uninit(reply, sizeof *ofs);
+ oxm_put_match(reply, &fs->match, version);
+ oxs_put_stat(reply, fs, version);
+ ofpacts_put_openflow_instructions(fs->ofpacts, fs->ofpacts_len, reply,
+ version);
+
+ ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs);
+ ofs->length = htons(reply->size - start_ofs);
+ ofs->table_id = fs->table_id;
+ ofs->priority = htons(fs->priority);
+ ofs->reason = 0;
+ memset(ofs->pad2, 0, sizeof ofs->pad2);
+ } else if (raw == OFPRAW_OFPST11_FLOW_REPLY ||
+ raw == OFPRAW_OFPST13_FLOW_REPLY) {
struct ofp11_flow_stats *ofs;
ofpbuf_put_uninit(reply, sizeof *ofs);
@@ -10150,6 +10265,7 @@ ofputil_is_bundlable(enum ofptype type)
case OFPTYPE_GET_ASYNC_REQUEST:
case OFPTYPE_DESC_STATS_REQUEST:
case OFPTYPE_FLOW_STATS_REQUEST:
+ case OFPTYPE_OXS_FLOW_STATS_REQUEST:
case OFPTYPE_AGGREGATE_STATS_REQUEST:
case OFPTYPE_TABLE_STATS_REQUEST:
case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
@@ -10178,6 +10294,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_OXS_FLOW_STATS_REPLY:
case OFPTYPE_QUEUE_STATS_REPLY:
case OFPTYPE_PORT_STATS_REPLY:
case OFPTYPE_TABLE_STATS_REPLY:
@@ -16,7 +16,6 @@
#include <config.h>
#include "nx-match.h"
-#include "ox-stat.h"
#include <netinet/icmp6.h>
#include "classifier.h"
#include "colors.h"
@@ -34,6 +33,7 @@
#include "tun-metadata.h"
#include "unaligned.h"
#include "util.h"
+#include "ox-stat.h"
VLOG_DEFINE_THIS_MODULE(ox_stat);
@@ -129,6 +129,8 @@ uint8_t oxs_field_set;
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);
+void oxs_put__(struct ofpbuf *, enum oxs_ofb_stat_fields,
+ enum ofp_version, const void *, const void *, size_t);
static bool is_experimenter_oxs(uint64_t header);
static int oxs_experimenter_len(uint64_t header);
static int oxs_payload_len (uint64_t header);
@@ -137,7 +139,20 @@ 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);
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 bool
is_experimenter_oxs(uint64_t header)
@@ -213,6 +228,181 @@ error:
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:
+ {
+ uint64_t duration = 0;
+
+ memcpy(&duration, payload, sizeof (duration));
+ duration = ntohll(duration);
+ fs->duration_sec = ((uint32_t) ((duration &
+ 0xFFFFFFFF00000000) >> 32));
+ fs->duration_nsec = ((uint32_t) (duration & 0xFFFFFFFF));
+ }
+ break;
+ case OFPXST_OFB_IDLE_TIME:
+ {
+ uint64_t idle_time = 0;
+
+ memcpy(&idle_time, payload, sizeof (idle_time));
+ idle_time = ntohll(idle_time);
+ fs->idle_age = ((idle_time & 0xFFFFFFFF00000000) >> 32);
+ }
+ break;
+ case OFPXST_OFB_PACKET_COUNT:
+ {
+ uint64_t packet_count;
+
+ memcpy(&packet_count, payload, sizeof (packet_count));
+ fs->packet_count = ntohll(packet_count);
+ }
+ break;
+ case OFPXST_OFB_BYTE_COUNT:
+ {
+ uint64_t 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)
+{
+ 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 {
+ if (field->id == OFPXST_OFB_DURATION) {
+ oxs_field_set |= 1 << 0;
+ } else if (field->id == OFPXST_OFB_IDLE_TIME) {
+ oxs_field_set |= 1 << 1;
+ } else if (field->id == OFPXST_OFB_FLOW_COUNT) {
+ oxs_field_set |= 1 << 2;
+ } else if (field->id == OFPXST_OFB_PACKET_COUNT) {
+ oxs_field_set |= 1 << 3;
+ } else if (field->id == OFPXST_OFB_BYTE_COUNT) {
+ oxs_field_set |= 1 << 4;
+ }
+ }
+ 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)
+{
+ 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);
+}
+
+static struct hmap oxs_header_map;
+static struct hmap oxs_name_map;
+
static void
oxs_init(void)
{
@@ -236,3 +426,176 @@ oxs_init(void)
}
}
+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)
+{
+ const size_t start_len = b->size;
+ int stat_len;
+
+ if (oxs_field_set & 1 << 0) {
+ uint64_t duration = 0;
+
+ if (fs) {
+ 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_field_set & 1 << 1) {
+ uint64_t idl_time = 0;
+
+ if (fs) {
+ idl_time = (uint64_t) fs->idle_age << 32;
+ idl_time = htonll(idl_time);
+ }
+ oxs_put__(b, OFPXST_OFB_IDLE_TIME, oxs, &idl_time, NULL,
+ OXS_STATS_IDLE_TIME_LEN);
+ }
+ if (oxs_field_set & 1 << 2) {
+ uint32_t flow_count = 0;
+
+ oxs_put__(b, OFPXST_OFB_FLOW_COUNT, oxs, &flow_count, NULL,
+ OXS_STATS_FLOW_COUNT_LEN);
+ }
+ if (oxs_field_set & 1 << 3) {
+ uint64_t pkt_count = 0;
+
+ if (fs) {
+ pkt_count = fs->packet_count;
+ pkt_count = htonll(pkt_count);
+ }
+ oxs_put__(b, OFPXST_OFB_PACKET_COUNT, oxs, &pkt_count, NULL,
+ OXS_STATS_PACKET_COUNT_LEN);
+ }
+ if (oxs_field_set & 1 << 4) {
+ uint64_t byte_count = 0;
+
+ if (fs) {
+ byte_count = fs->byte_count;
+ byte_count = htonll(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)
+{
+ 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)
+ + 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;
+}
+
@@ -28,5 +28,6 @@
int oxs_put_stat(struct ofpbuf *, const struct ofputil_flow_stats *,
enum ofp_version );
-
+int oxs_pull_stat(struct ofpbuf *,struct ofputil_flow_stats *,
+ uint16_t *);
#endif /* ox_stat.h */
@@ -7004,7 +7004,6 @@ flow_mods_reset_counts () {
# OpenFlow versions >= 1.3 should behave the same way
flow_mods_reset_counts 13
flow_mods_reset_counts 14
-flow_mods_reset_counts 15
OVS_VSWITCHD_STOP
AT_CLEANUP
@@ -1392,10 +1392,6 @@ 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
])
-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
-])
OVS_VSWITCHD_STOP
AT_CLEANUP