+static int desc_stats_dump(struct datapath *dp, void *state,
+ struct ofpbuf *buffer)
+{
+ struct ofp_desc_stats *ods = ofpbuf_put_zeros(buffer, sizeof *ods);
+
+ strncpy(ods->mfr_desc, &mfr_desc, sizeof ods->mfr_desc);
+ strncpy(ods->hw_desc, &hw_desc, sizeof ods->hw_desc);
+ strncpy(ods->sw_desc, &sw_desc, sizeof ods->sw_desc);
+ strncpy(ods->serial_num, &serial_num, sizeof ods->serial_num);
+
+ return 0;
+}
+
+struct flow_stats_state {
+ int table_idx;
+ struct sw_table_position position;
+ struct ofp_flow_stats_request rq;
+ time_t now;
+
+ struct ofpbuf *buffer;
+};
+
+#define MAX_FLOW_STATS_BYTES 4096
+
+static int flow_stats_init(struct datapath *dp, const void *body, int body_len,
+ void **state)
+{
+ const struct ofp_flow_stats_request *fsr = body;
+ struct flow_stats_state *s = xmalloc(sizeof *s);
+ s->table_idx = fsr->table_id == 0xff ? 0 : fsr->table_id;
+ memset(&s->position, 0, sizeof s->position);
+ s->rq = *fsr;
+ *state = s;
+ return 0;
+}
+
+static int flow_stats_dump_callback(struct sw_flow *flow, void *private)
+{
+ struct flow_stats_state *s = private;
+ fill_flow_stats(s->buffer, flow, s->table_idx, s->now);
+ return s->buffer->size >= MAX_FLOW_STATS_BYTES;
+}
+
+static int flow_stats_dump(struct datapath *dp, void *state,
+ struct ofpbuf *buffer)
+{
+ struct flow_stats_state *s = state;
+ struct sw_flow_key match_key;
+
+ flow_extract_match(&match_key, &s->rq.match);
+ s->buffer = buffer;
+ s->now = time_now();
+ while (s->table_idx < dp->chain->n_tables
+ && (s->rq.table_id == 0xff || s->rq.table_id == s->table_idx))
+ {
+ struct sw_table *table = dp->chain->tables[s->table_idx];
+
+ if (table->iterate(table, &match_key, s->rq.out_port,
+ &s->position, flow_stats_dump_callback, s))
+ break;
+
+ s->table_idx++;
+ memset(&s->position, 0, sizeof s->position);
+ }
+ return s->buffer->size >= MAX_FLOW_STATS_BYTES;
+}
+
+static void flow_stats_done(void *state)
+{
+ free(state);
+}
+
+struct aggregate_stats_state {
+ struct ofp_aggregate_stats_request rq;
+};
+
+static int aggregate_stats_init(struct datapath *dp,
+ const void *body, int body_len,
+ void **state)
+{
+ const struct ofp_aggregate_stats_request *rq = body;
+ struct aggregate_stats_state *s = xmalloc(sizeof *s);
+ s->rq = *rq;
+ *state = s;
+ return 0;
+}
+
+static int aggregate_stats_dump_callback(struct sw_flow *flow, void *private)
+{
+ struct ofp_aggregate_stats_reply *rpy = private;
+ rpy->packet_count += flow->packet_count;
+ rpy->byte_count += flow->byte_count;
+ rpy->flow_count++;
+ return 0;
+}
+
+static int aggregate_stats_dump(struct datapath *dp, void *state,
+ struct ofpbuf *buffer)
+{
+ struct aggregate_stats_state *s = state;
+ struct ofp_aggregate_stats_request *rq = &s->rq;
+ struct ofp_aggregate_stats_reply *rpy;
+ struct sw_table_position position;
+ struct sw_flow_key match_key;
+ int table_idx;
+
+ rpy = ofpbuf_put_zeros(buffer, sizeof *rpy);
+
+ flow_extract_match(&match_key, &rq->match);
+ table_idx = rq->table_id == 0xff ? 0 : rq->table_id;
+ memset(&position, 0, sizeof position);
+ while (table_idx < dp->chain->n_tables
+ && (rq->table_id == 0xff || rq->table_id == table_idx))
+ {
+ struct sw_table *table = dp->chain->tables[table_idx];
+ int error;
+
+ error = table->iterate(table, &match_key, rq->out_port, &position,
+ aggregate_stats_dump_callback, rpy);
+ if (error)
+ return error;
+
+ table_idx++;
+ memset(&position, 0, sizeof position);
+ }
+
+ rpy->packet_count = htonll(rpy->packet_count);
+ rpy->byte_count = htonll(rpy->byte_count);
+ rpy->flow_count = htonl(rpy->flow_count);
+ return 0;
+}
+
+static void aggregate_stats_done(void *state)
+{
+ free(state);
+}
+
+static int table_stats_dump(struct datapath *dp, void *state,
+ struct ofpbuf *buffer)
+{
+ int i;
+ for (i = 0; i < dp->chain->n_tables; i++) {
+ struct ofp_table_stats *ots = ofpbuf_put_zeros(buffer, sizeof *ots);
+ struct sw_table_stats stats;
+ dp->chain->tables[i]->stats(dp->chain->tables[i], &stats);
+ strncpy(ots->name, stats.name, sizeof ots->name);
+ ots->table_id = i;
+ ots->wildcards = htonl(stats.wildcards);
+ ots->max_entries = htonl(stats.max_flows);
+ ots->active_count = htonl(stats.n_flows);
+ ots->lookup_count = htonll(stats.n_lookup);
+ ots->matched_count = htonll(stats.n_matched);
+ }
+ return 0;
+}
+
+struct port_stats_state {
+ int port;
+};
+
+static int port_stats_init(struct datapath *dp, const void *body, int body_len,
+ void **state)
+{
+ struct port_stats_state *s = xmalloc(sizeof *s);
+ s->port = 0;
+ *state = s;
+ return 0;
+}
+
+static int port_stats_dump(struct datapath *dp, void *state,
+ struct ofpbuf *buffer)
+{
+ struct port_stats_state *s = state;
+ int i;
+
+ for (i = s->port; i < DP_MAX_PORTS; i++) {
+ struct sw_port *p = &dp->ports[i];
+ struct ofp_port_stats *ops;
+ if (!p->netdev) {
+ continue;
+ }
+ ops = ofpbuf_put_zeros(buffer, sizeof *ops);
+ ops->port_no = htons(port_no(dp, p));
+ ops->rx_packets = htonll(p->rx_packets);
+ ops->tx_packets = htonll(p->tx_packets);
+ ops->rx_bytes = htonll(p->rx_bytes);
+ ops->tx_bytes = htonll(p->tx_bytes);
+ ops->rx_dropped = htonll(-1);
+ ops->tx_dropped = htonll(p->tx_dropped);
+ ops->rx_errors = htonll(-1);
+ ops->tx_errors = htonll(-1);
+ ops->rx_frame_err = htonll(-1);
+ ops->rx_over_err = htonll(-1);
+ ops->rx_crc_err = htonll(-1);
+ ops->collisions = htonll(-1);
+ ops++;
+ }
+ s->port = i;
+ return 0;
+}
+
+static void port_stats_done(void *state)
+{
+ free(state);
+}
+
+struct stats_type {
+ /* Value for 'type' member of struct ofp_stats_request. */
+ int type;
+
+ /* Minimum and maximum acceptable number of bytes in body member of
+ * struct ofp_stats_request. */
+ size_t min_body, max_body;
+
+ /* Prepares to dump some kind of statistics on 'dp'. 'body' and
+ * 'body_len' are the 'body' member of the struct ofp_stats_request.
+ * Returns zero if successful, otherwise a negative error code.
+ * May initialize '*state' to state information. May be null if no
+ * initialization is required.*/
+ int (*init)(struct datapath *dp, const void *body, int body_len,
+ void **state);
+
+ /* Appends statistics for 'dp' to 'buffer', which initially contains a
+ * struct ofp_stats_reply. On success, it should return 1 if it should be
+ * called again later with another buffer, 0 if it is done, or a negative
+ * errno value on failure. */
+ int (*dump)(struct datapath *dp, void *state, struct ofpbuf *buffer);
+
+ /* Cleans any state created by the init or dump functions. May be null
+ * if no cleanup is required. */
+ void (*done)(void *state);
+};
+
+static const struct stats_type stats[] = {
+ {
+ OFPST_DESC,
+ 0,
+ 0,
+ NULL,
+ desc_stats_dump,
+ NULL
+ },
+ {
+ OFPST_FLOW,
+ sizeof(struct ofp_flow_stats_request),
+ sizeof(struct ofp_flow_stats_request),
+ flow_stats_init,
+ flow_stats_dump,
+ flow_stats_done
+ },
+ {
+ OFPST_AGGREGATE,
+ sizeof(struct ofp_aggregate_stats_request),
+ sizeof(struct ofp_aggregate_stats_request),
+ aggregate_stats_init,
+ aggregate_stats_dump,
+ aggregate_stats_done
+ },
+ {
+ OFPST_TABLE,
+ 0,
+ 0,
+ NULL,
+ table_stats_dump,
+ NULL
+ },
+ {
+ OFPST_PORT,
+ 0,
+ 0,
+ port_stats_init,
+ port_stats_dump,
+ port_stats_done
+ },
+};
+
+struct stats_dump_cb {
+ bool done;
+ struct ofp_stats_request *rq;
+ struct sender sender;
+ const struct stats_type *s;
+ void *state;
+};
+
+static int
+stats_dump(struct datapath *dp, void *cb_)
+{
+ struct stats_dump_cb *cb = cb_;
+ struct ofp_stats_reply *osr;
+ struct ofpbuf *buffer;
+ int err;
+
+ if (cb->done) {
+ return 0;
+ }
+
+ osr = make_openflow_reply(sizeof *osr, OFPT_STATS_REPLY, &cb->sender,
+ &buffer);
+ osr->type = htons(cb->s->type);
+ osr->flags = 0;
+
+ err = cb->s->dump(dp, cb->state, buffer);
+ if (err >= 0) {
+ int err2;
+ if (!err) {
+ cb->done = true;
+ } else {
+ /* Buffer might have been reallocated, so find our data again. */
+ osr = ofpbuf_at_assert(buffer, 0, sizeof *osr);
+ osr->flags = ntohs(OFPSF_REPLY_MORE);
+ }
+ err2 = send_openflow_buffer(dp, buffer, &cb->sender);
+ if (err2) {
+ err = err2;
+ }
+ }
+
+ return err;
+}
+
+static void
+stats_done(void *cb_)
+{
+ struct stats_dump_cb *cb = cb_;
+ if (cb) {
+ if (cb->s->done) {
+ cb->s->done(cb->state);
+ }
+ free(cb);
+ }
+}
+
+static int
+recv_stats_request(struct datapath *dp, const struct sender *sender,
+ const void *oh)
+{
+ const struct ofp_stats_request *rq = oh;
+ size_t rq_len = ntohs(rq->header.length);
+ const struct stats_type *st;
+ struct stats_dump_cb *cb;
+ int type, body_len;
+ int err;
+
+ type = ntohs(rq->type);
+ for (st = stats; ; st++) {
+ if (st >= &stats[ARRAY_SIZE(stats)]) {
+ VLOG_WARN_RL(&rl, "received stats request of unknown type %d",
+ type);
+ return -EINVAL;
+ } else if (type == st->type) {
+ break;
+ }
+ }
+
+ cb = xmalloc(sizeof *cb);
+ cb->done = false;
+ cb->rq = xmemdup(rq, rq_len);
+ cb->sender = *sender;
+ cb->s = st;
+ cb->state = NULL;
+
+ body_len = rq_len - offsetof(struct ofp_stats_request, body);
+ if (body_len < cb->s->min_body || body_len > cb->s->max_body) {
+ VLOG_WARN_RL(&rl, "stats request type %d with bad body length %d",
+ type, body_len);
+ err = -EINVAL;
+ goto error;
+ }
+
+ if (cb->s->init) {
+ err = cb->s->init(dp, rq->body, body_len, &cb->state);
+ if (err) {
+ VLOG_WARN_RL(&rl,
+ "failed initialization of stats request type %d: %s",
+ type, strerror(-err));
+ goto error;
+ }
+ }
+
+ remote_start_dump(sender->remote, stats_dump, stats_done, cb);
+ return 0;
+
+error:
+ free(cb->rq);
+ free(cb);
+ return err;
+}
+
+static int
+recv_echo_request(struct datapath *dp, const struct sender *sender,
+ const void *oh)
+{
+ return send_openflow_buffer(dp, make_echo_reply(oh), sender);
+}
+
+static int
+recv_echo_reply(struct datapath *dp UNUSED, const struct sender *sender UNUSED,
+ const void *oh UNUSED)
+{
+ return 0;
+}
+