+static void
+vendor_stat(struct ds *string, const void *body, size_t len,
+ int verbosity UNUSED)
+{
+ ds_put_format(string, " vendor=%08"PRIx32, ntohl(*(uint32_t *) body));
+ ds_put_format(string, " %zu bytes additional data",
+ len - sizeof(uint32_t));
+}
+
+enum stats_direction {
+ REQUEST,
+ REPLY
+};
+
+static void
+print_stats(struct ds *string, int type, const void *body, size_t body_len,
+ int verbosity, enum stats_direction direction)
+{
+ struct stats_msg {
+ size_t min_body, max_body;
+ void (*printer)(struct ds *, const void *, size_t len, int verbosity);
+ };
+
+ struct stats_type {
+ int type;
+ const char *name;
+ struct stats_msg request;
+ struct stats_msg reply;
+ };
+
+ static const struct stats_type stats_types[] = {
+ {
+ OFPST_DESC,
+ "description",
+ { 0, 0, NULL },
+ { 0, SIZE_MAX, ofp_desc_stats_reply },
+ },
+ {
+ OFPST_FLOW,
+ "flow",
+ { sizeof(struct ofp_flow_stats_request),
+ sizeof(struct ofp_flow_stats_request),
+ ofp_flow_stats_request },
+ { 0, SIZE_MAX, ofp_flow_stats_reply },
+ },
+ {
+ OFPST_AGGREGATE,
+ "aggregate",
+ { sizeof(struct ofp_aggregate_stats_request),
+ sizeof(struct ofp_aggregate_stats_request),
+ ofp_aggregate_stats_request },
+ { sizeof(struct ofp_aggregate_stats_reply),
+ sizeof(struct ofp_aggregate_stats_reply),
+ ofp_aggregate_stats_reply },
+ },
+ {
+ OFPST_TABLE,
+ "table",
+ { 0, 0, NULL },
+ { 0, SIZE_MAX, ofp_table_stats_reply },
+ },
+ {
+ OFPST_PORT,
+ "port",
+ { 0, 0, NULL, },
+ { 0, SIZE_MAX, ofp_port_stats_reply },
+ },
+ {
+ OFPST_VENDOR,
+ "vendor-specific",
+ { sizeof(uint32_t), SIZE_MAX, vendor_stat },
+ { sizeof(uint32_t), SIZE_MAX, vendor_stat },
+ },
+ {
+ -1,
+ "unknown",
+ { 0, 0, NULL, },
+ { 0, 0, NULL, },
+ },
+ };
+
+ const struct stats_type *s;
+ const struct stats_msg *m;
+
+ if (type >= ARRAY_SIZE(stats_types) || !stats_types[type].name) {
+ ds_put_format(string, " ***unknown type %d***", type);
+ return;
+ }
+ for (s = stats_types; s->type >= 0; s++) {
+ if (s->type == type) {
+ break;
+ }
+ }
+ ds_put_format(string, " type=%d(%s)\n", type, s->name);
+
+ m = direction == REQUEST ? &s->request : &s->reply;
+ if (body_len < m->min_body || body_len > m->max_body) {
+ ds_put_format(string, " ***body_len=%zu not in %zu...%zu***",
+ body_len, m->min_body, m->max_body);
+ return;
+ }
+ if (m->printer) {
+ m->printer(string, body, body_len, verbosity);
+ }
+}
+
+static void
+ofp_stats_request(struct ds *string, const void *oh, size_t len, int verbosity)
+{
+ const struct ofp_stats_request *srq = oh;
+
+ if (srq->flags) {
+ ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***",
+ ntohs(srq->flags));
+ }
+
+ print_stats(string, ntohs(srq->type), srq->body,
+ len - offsetof(struct ofp_stats_request, body),
+ verbosity, REQUEST);
+}
+
+static void
+ofp_stats_reply(struct ds *string, const void *oh, size_t len, int verbosity)
+{
+ const struct ofp_stats_reply *srp = oh;
+
+ ds_put_cstr(string, " flags=");
+ if (!srp->flags) {
+ ds_put_cstr(string, "none");
+ } else {
+ uint16_t flags = ntohs(srp->flags);
+ if (flags & OFPSF_REPLY_MORE) {
+ ds_put_cstr(string, "[more]");
+ flags &= ~OFPSF_REPLY_MORE;
+ }
+ if (flags) {
+ ds_put_format(string, "[***unknown flags 0x%04"PRIx16"***]", flags);
+ }
+ }
+
+ print_stats(string, ntohs(srp->type), srp->body,
+ len - offsetof(struct ofp_stats_reply, body),
+ verbosity, REPLY);
+}
+
+static void
+ofp_echo(struct ds *string, const void *oh, size_t len, int verbosity)
+{
+ const struct ofp_header *hdr = oh;
+
+ ds_put_format(string, " %zu bytes of payload\n", len - sizeof *hdr);
+ if (verbosity > 1) {
+ ds_put_hex_dump(string, hdr, len - sizeof *hdr, 0, true);
+ }
+}
+