@@ -3346,7 +3346,23 @@ 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;
+ } else if (raw == OFPRAW_OFPT11_FLOW_REMOVED) {
const struct ofp12_flow_removed *ofr;
enum ofperr error;
@@ -3429,12 +3445,29 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
}
switch (protocol) {
+ 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;
+ ofputil_put_ofp11_match(msg, &fr->match, protocol);
+ /*Stats encoding in OXS TLV Format*/
+ oxs_flow_removed_stat_put(msg,fr,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 ofp12_flow_removed *ofr;
msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED,
@@ -150,6 +150,11 @@ static enum ofperr oxs_pull_raw(const uint8_t *, unsigned int ,
ovs_be64 * cookie, ovs_be64 * cookie_mask);
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,
@@ -157,6 +162,18 @@ static void oxs_put_header_len(struct ofpbuf *b,
enum ofp_version version);
static int ox_put_agg_raw(struct ofpbuf *b, enum ofp_version oxs,
const struct ofputil_aggregate_stats *fs);
+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);
static bool
is_experimenter_oxs(uint64_t header)
@@ -765,3 +782,211 @@ oxs_put_agg_stat(struct ofpbuf *b, const struct ofputil_aggregate_stats *fs,
return stat_len;
}
+static void
+oxs_put_duration(struct ofpbuf *b,
+ const struct ofputil_flow_removed *fr,
+ enum ofp_version version)
+{
+ uint64_t duration = 0;
+
+ if (fr) {
+ 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)
+{
+ uint64_t pkt_count = 0;
+
+ if (fr) {
+ pkt_count = fr->packet_count;
+ pkt_count = htonll(pkt_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)
+{
+ uint64_t byte_count = 0;
+
+ if (fr) {
+ byte_count = fr->byte_count;
+ byte_count = htonll(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:
+ {
+ uint64_t duration;
+
+ memcpy(&duration, payload, sizeof (duration));
+ fr->duration_nsec = ntohl((duration & 0xffffffff));
+ fr->duration_sec = ntohl((uint32_t) (duration >> 32));
+ }
+ break;
+ case OFPXST_OFB_PACKET_COUNT:
+ {
+ uint64_t packet_count;
+
+ memcpy(&packet_count, payload, sizeof (packet_count));
+ fr->packet_count = ntohll(packet_count);
+
+ }
+ break;
+ case OFPXST_OFB_BYTE_COUNT:
+ {
+ uint64_t 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);
+
+}
+