@@ -150,4 +150,54 @@ struct ofp15_group_desc_stats {
};
OFP_ASSERT(sizeof(struct ofp15_group_desc_stats) == 16);
+struct ofp_oxs_stat {
+ ovs_be16 reserved; /* One of OFPST_* */
+ 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_oxs_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_oxs_flow_stats_request) == 32);
+
+/* Body of reply to OFPMP_FLOW_STATS request
+* and body for OFPIT_STAT_TRIGGER generated status. */
+struct ofp15_oxs_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_oxs_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 = 2, /* Number of aggregated flow entries. */
+ OFPXST_OFB_PACKET_COUNT = 3, /* Number of packets in flow entry. */
+ OFPXST_OFB_BYTE_COUNT = 4, /* Number of bytes in flow entry. */
+};
+
#endif /* openflow/openflow-1.5.h */
@@ -287,6 +287,8 @@ enum ofpraw {
OFPRAW_OFPST10_FLOW_REQUEST,
/* OFPST 1.1+ (1): struct ofp11_flow_stats_request, uint8_t[8][]. */
OFPRAW_OFPST11_FLOW_REQUEST,
+ /* OFPST 1.5+ (17): struct ofp15_oxs_flow_stats_request, uint8_t[8][]. */
+ OFPRAW_OFPST15_OXS_FLOW_REQUEST,
/* NXST 1.0 (0): struct nx_flow_stats_request, uint8_t[8][]. */
OFPRAW_NXST_FLOW_REQUEST,
@@ -296,6 +298,8 @@ enum ofpraw {
OFPRAW_OFPST11_FLOW_REPLY,
/* OFPST 1.3+ (1): uint8_t[]. */
OFPRAW_OFPST13_FLOW_REPLY,
+ /* OFPST 1.5+ (17): uint8_t[]. */
+ OFPRAW_OFPST15_OXS_FLOW_REPLY,
/* NXST 1.0 (0): uint8_t[]. */
OFPRAW_NXST_FLOW_REPLY,
@@ -628,6 +632,7 @@ enum ofptype {
OFPTYPE_FLOW_STATS_REQUEST, /* OFPRAW_OFPST10_FLOW_REQUEST.
* OFPRAW_OFPST11_FLOW_REQUEST.
* OFPRAW_NXST_FLOW_REQUEST. */
+ OFPTYPE_OXS_FLOW_STATS_REQUEST, /* OFPRAW_OFPST15_OXS_FLOW_REQUEST. */
OFPTYPE_FLOW_STATS_REPLY, /* OFPRAW_OFPST10_FLOW_REPLY.
* OFPRAW_OFPST11_FLOW_REPLY.
* OFPRAW_OFPST13_FLOW_REPLY.
@@ -635,6 +640,7 @@ enum ofptype {
OFPTYPE_AGGREGATE_STATS_REQUEST, /* OFPRAW_OFPST10_AGGREGATE_REQUEST.
* OFPRAW_OFPST11_AGGREGATE_REQUEST.
* OFPRAW_NXST_AGGREGATE_REQUEST. */
+ OFPTYPE_OXS_FLOW_STATS_REPLY, /* OFPRAW_OFPST15_OXS_FLOW_REPLY. */
OFPTYPE_AGGREGATE_STATS_REPLY, /* OFPRAW_OFPST_AGGREGATE_REPLY.
* OFPRAW_NXST_AGGREGATE_REPLY. */
OFPTYPE_TABLE_STATS_REQUEST, /* OFPRAW_OFPST_TABLE_REQUEST. */
@@ -197,6 +197,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 \
@@ -41,6 +41,8 @@
#include "socket-util.h"
#include "util.h"
+extern uint8_t oxs_field_set;
+
/* Parses 'str' as an 8-bit unsigned integer into '*valuep'.
*
* 'name' describes the value parsed in an error message, if any.
@@ -188,6 +190,34 @@ str_to_connhelper(const char *str, uint16_t *alg)
return xasprintf("invalid conntrack helper \"%s\"", str);
}
+struct ox_fields {
+ const char *name;
+ uint16_t fl_type;
+};
+
+static bool
+parse_oxs_field(const char *name, const struct ox_fields **f_out)
+{
+ static const struct ox_fields fields[] = {
+ {"oxs-duration", OFPXST_OFB_DURATION},
+ {"oxs-idle_time", OFPXST_OFB_IDLE_TIME},
+ {"oxs-flow_count", OFPXST_OFB_FLOW_COUNT},
+ {"oxs-packet_count", OFPXST_OFB_PACKET_COUNT},
+ {"oxs-byte_count", OFPXST_OFB_BYTE_COUNT},
+ };
+
+ const struct ox_fields *f;
+
+ for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
+ if (!strcmp(f->name, name)) {
+ *f_out = f;
+ return true;
+ }
+ }
+ *f_out = NULL;
+ return false;
+}
+
struct protocol {
const char *name;
uint16_t dl_type;
@@ -406,10 +436,23 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
while (ofputil_parse_key_value(&string, &name, &value)) {
const struct protocol *p;
+ const struct ox_fields *f;
const struct mf_field *mf;
char *error = NULL;
- if (parse_protocol(name, &p)) {
+ if (parse_oxs_field(name, &f)) {
+ if (f->fl_type == OFPXST_OFB_DURATION) {
+ oxs_field_set |= (1 << f->fl_type);
+ } else if (f->fl_type == OFPXST_OFB_IDLE_TIME) {
+ oxs_field_set |= (1 << f->fl_type);
+ } else if (f->fl_type == OFPXST_OFB_FLOW_COUNT) {
+ oxs_field_set |= (1 << f->fl_type);
+ } else if (f->fl_type == OFPXST_OFB_PACKET_COUNT) {
+ oxs_field_set |= (1 << f->fl_type);
+ } else if (f->fl_type == OFPXST_OFB_BYTE_COUNT) {
+ oxs_field_set |= (1 << f->fl_type);
+ }
+ } else if (parse_protocol(name, &p)) {
match_set_dl_type(&fm->match, htons(p->dl_type));
if (p->nw_proto) {
match_set_nw_proto(&fm->match, p->nw_proto);
@@ -3563,6 +3563,11 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
ofp_print_flow_stats_request(string, oh);
break;
+ case OFPTYPE_OXS_FLOW_STATS_REQUEST:
+ ofp_print_stats(string, oh);
+ ofp_print_flow_stats_request(string, oh);
+ break;
+
case OFPTYPE_TABLE_STATS_REQUEST:
ofp_print_stats(string, oh);
break;
@@ -3587,6 +3592,11 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
ofp_print_flow_stats_reply(string, oh);
break;
+ case OFPTYPE_OXS_FLOW_STATS_REPLY:
+ ofp_print_stats(string, oh);
+ ofp_print_flow_stats_reply(string, oh);
+ break;
+
case OFPTYPE_QUEUE_STATS_REPLY:
ofp_print_stats(string, oh);
ofp_print_ofpst_queue_reply(string, oh, verbosity);
new file mode 100644
@@ -0,0 +1,238 @@
+/*
+ * 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 "ox-stat.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"
+
+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}},
+};
+
+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);
+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 void oxs_init(void);
+
+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 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);
+ }
+}
+
new file mode 100644
@@ -0,0 +1,32 @@
+/*
+ * 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"
+
+int oxs_put_stat(struct ofpbuf *, const struct ofputil_flow_stats *,
+ enum ofp_version );
+
+#endif /* ox_stat.h */
@@ -1392,6 +1392,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_OXS_FLOW_STATS_REQUEST:
+ case OFPTYPE_OXS_FLOW_STATS_REPLY:
case OFPTYPE_AGGREGATE_STATS_REQUEST:
case OFPTYPE_AGGREGATE_STATS_REPLY:
case OFPTYPE_TABLE_STATS_REQUEST:
@@ -8092,6 +8092,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
case OFPTYPE_FLOW_STATS_REQUEST:
return handle_flow_stats_request(ofconn, oh);
+ case OFPTYPE_OXS_FLOW_STATS_REQUEST:
+ return handle_flow_stats_request(ofconn, oh);
+
case OFPTYPE_AGGREGATE_STATS_REQUEST:
return handle_aggregate_stats_request(ofconn, oh);
@@ -8167,6 +8170,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_OXS_FLOW_STATS_REPLY:
case OFPTYPE_QUEUE_STATS_REPLY:
case OFPTYPE_PORT_STATS_REPLY:
case OFPTYPE_TABLE_STATS_REPLY: