memset(ofs->pad, 0, sizeof ofs->pad);
}
-static int
-fill_port_stats_reply(struct datapath *dp, struct ofp_port_stats_reply *psr)
-{
- struct net_bridge_port *p;
- int port_count = 0;
-
- list_for_each_entry_rcu (p, &dp->port_list, node) {
- struct ofp_port_stats *ps = &psr->ports[port_count++];
- struct net_device_stats *stats = p->dev->get_stats(p->dev);
- ps->port_no = htons(p->port_no);
- memset(ps->pad, 0, sizeof ps->pad);
- ps->rx_count = cpu_to_be64(stats->rx_packets);
- ps->tx_count = cpu_to_be64(stats->tx_packets);
- ps->drop_count = cpu_to_be64(stats->rx_dropped
- + stats->tx_dropped);
- }
-
- return port_count;
-}
-
-int
-dp_send_port_stats(struct datapath *dp, const struct sender *sender)
-{
- struct sk_buff *skb;
- struct ofp_port_stats_reply *psr;
- size_t psr_len, port_max_len;
- int port_count;
-
- /* Overallocate. */
- port_max_len = sizeof(struct ofp_port_stats) * OFPP_MAX;
- psr = alloc_openflow_skb(dp, sizeof *psr + port_max_len,
- OFPT_PORT_STATS_REPLY, sender, &skb);
- if (!psr)
- return -ENOMEM;
-
- /* Fill. */
- port_count = fill_port_stats_reply(dp, psr);
-
- /* Shrink to fit. */
- psr_len = sizeof *psr + sizeof(struct ofp_port_stats) * port_count;
- resize_openflow_skb(skb, &psr->header, psr_len);
- return send_openflow_skb(skb, sender);
-}
-
-int
-dp_send_table_stats(struct datapath *dp, const struct sender *sender)
-{
- struct sk_buff *skb;
- struct ofp_table_stats_reply *tsr;
- int i, n_tables;
-
- n_tables = dp->chain->n_tables;
- tsr = alloc_openflow_skb(dp, (offsetof(struct ofp_table_stats_reply,
- tables)
- + sizeof tsr->tables[0] * n_tables),
- OFPT_TABLE_STATS_REPLY, sender, &skb);
- if (!tsr)
- return -ENOMEM;
- for (i = 0; i < n_tables; i++) {
- struct ofp_table_stats *ots = &tsr->tables[i];
- 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->pad[0] = ots->pad[1] = 0;
- ots->max_entries = htonl(stats.max_flows);
- ots->active_count = htonl(stats.n_flows);
- ots->matched_count = cpu_to_be64(0); /* FIXME */
- }
- return send_openflow_skb(skb, sender);
-}
-
/* Generic Netlink interface.
*
* See netlink(7) for an introduction to netlink. See
[DP_GENL_A_DP_IDX] = { .type = NLA_U32 },
};
-struct flow_stats_cb_state {
- int dp_idx;
+struct flow_stats_state {
int table_idx;
struct sw_table_position position;
- struct ofp_flow_stats_request *rq;
- int sent_terminator;
+ const struct ofp_flow_stats_request *rq;
struct ofp_flow_stats *flows;
int n_flows, max_flows;
};
-static int muster_callback(struct sw_flow *flow, void *private)
+static int flow_stats_init(struct datapath *dp, const void *body, int body_len,
+ void **state)
{
- struct flow_stats_cb_state *s = private;
+ const struct ofp_flow_stats_request *fsr = body;
+ struct flow_stats_state *s = kmalloc(sizeof *s, GFP_ATOMIC);
+ if (!s)
+ return -ENOMEM;
+ 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->flows[s->n_flows], flow, s->table_idx);
return ++s->n_flows >= s->max_flows;
}
-int
-muster_flow_stats(struct datapath *dp, struct flow_stats_cb_state *s,
- const struct sender *sender, struct sk_buff *skb)
+static int flow_stats_dump(struct datapath *dp, void *state,
+ void *body, int *body_len)
{
- struct ofp_flow_stats_reply *fsr;
- size_t header_size, flow_size;
+ struct flow_stats_state *s = state;
+ struct ofp_flow_stats *ofs;
struct sw_flow_key match_key;
- int max_openflow_len;
- size_t size;
- fsr = put_openflow_headers(dp, skb, OFPT_FLOW_STATS_REPLY, sender,
- &max_openflow_len);
- if (IS_ERR(fsr))
- return PTR_ERR(fsr);
- resize_openflow_skb(skb, &fsr->header, max_openflow_len);
-
- header_size = offsetof(struct ofp_flow_stats_reply, flows);
- flow_size = sizeof fsr->flows[0];
- s->max_flows = (max_openflow_len - header_size) / flow_size;
- if (s->max_flows <= 0)
+ s->max_flows = *body_len / sizeof *ofs;
+ if (!s->max_flows)
return -ENOMEM;
- s->flows = fsr->flows;
+ s->flows = body;
flow_extract_match(&match_key, &s->rq->match);
s->n_flows = 0;
struct sw_table *table = dp->chain->tables[s->table_idx];
if (table->iterate(table, &match_key, &s->position,
- muster_callback, s))
+ flow_stats_dump_callback, s))
break;
s->table_idx++;
memset(&s->position, 0, sizeof s->position);
}
- if (!s->n_flows) {
- /* Signal dump completion. */
- if (s->sent_terminator) {
- return 0;
- }
- s->sent_terminator = 1;
+ *body_len = sizeof *ofs * s->n_flows;
+ return s->n_flows >= s->max_flows;
+}
+
+static void flow_stats_done(void *state)
+{
+ kfree(state);
+}
+
+static int table_stats_dump(struct datapath *dp, void *state,
+ void *body, int *body_len)
+{
+ struct ofp_table_stats *ots;
+ int nbytes = dp->chain->n_tables * sizeof *ots;
+ int i;
+ if (nbytes > *body_len)
+ return -ENOBUFS;
+ *body_len = nbytes;
+ for (i = 0, ots = body; i < dp->chain->n_tables; i++, 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;
+ memset(ots->pad, 0, sizeof ots->pad);
+ ots->max_entries = htonl(stats.max_flows);
+ ots->active_count = htonl(stats.n_flows);
+ ots->matched_count = cpu_to_be64(0); /* FIXME */
}
- size = header_size + flow_size * s->n_flows;
- resize_openflow_skb(skb, &fsr->header, size);
- return skb->len;
+ 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 = kmalloc(sizeof *s, GFP_ATOMIC);
+ if (!s)
+ return -ENOMEM;
+ s->port = 0;
+ *state = s;
+ return 0;
}
+static int port_stats_dump(struct datapath *dp, void *state,
+ void *body, int *body_len)
+{
+ struct port_stats_state *s = state;
+ struct ofp_port_stats *ops;
+ int n_ports, max_ports;
+ int i;
+
+ max_ports = *body_len / sizeof *ops;
+ if (!max_ports)
+ return -ENOMEM;
+ ops = body;
+
+ n_ports = 0;
+ for (i = s->port; i < OFPP_MAX && n_ports < max_ports; i++) {
+ struct net_bridge_port *p = dp->ports[i];
+ struct net_device_stats *stats;
+ if (!p)
+ continue;
+ stats = p->dev->get_stats(p->dev);
+ ops->port_no = htons(p->port_no);
+ memset(ops->pad, 0, sizeof ops->pad);
+ ops->rx_count = cpu_to_be64(stats->rx_packets);
+ ops->tx_count = cpu_to_be64(stats->tx_packets);
+ ops->drop_count = cpu_to_be64(stats->rx_dropped
+ + stats->tx_dropped);
+ n_ports++;
+ ops++;
+ }
+ s->port = i;
+ *body_len = n_ports * sizeof *ops;
+ return n_ports >= max_ports;
+}
+
+static void port_stats_done(void *state)
+{
+ kfree(state);
+}
+
+struct stats_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);
+
+ /* Dumps statistics for 'dp' into the '*body_len' bytes at 'body', and
+ * modifies '*body_len' to reflect the number of bytes actually used.
+ * ('body' will be transmitted as the 'body' member of struct
+ * ofp_stats_reply.) */
+ int (*dump)(struct datapath *dp, void *state,
+ void *body, int *body_len);
+
+ /* 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_FLOW] = {
+ sizeof(struct ofp_flow_stats_request),
+ sizeof(struct ofp_flow_stats_request),
+ flow_stats_init,
+ flow_stats_dump,
+ flow_stats_done
+ },
+ [OFPST_TABLE] = {
+ 0,
+ 0,
+ NULL,
+ table_stats_dump,
+ NULL
+ },
+ [OFPST_PORT] = {
+ 0,
+ 0,
+ port_stats_init,
+ port_stats_dump,
+ port_stats_done
+ },
+};
+
static int
dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
{
struct datapath *dp;
struct sender sender;
- struct flow_stats_cb_state *state;
+ const struct stats_type *s;
+ struct ofp_stats_reply *osr;
+ int dp_idx;
+ int max_openflow_len, body_len;
+ void *body;
int err;
+ rcu_read_lock();
if (!cb->args[0]) {
struct nlattr *attrs[DP_GENL_A_MAX + 1];
- struct ofp_flow_stats_request *rq;
+ struct ofp_stats_request *rq;
struct nlattr *va;
+ size_t len, body_len;
+ int type;
err = nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs, DP_GENL_A_MAX,
dp_genl_openflow_policy);
if (err < 0)
return err;
+ err = -EINVAL;
+
if (!attrs[DP_GENL_A_DP_IDX])
- return -EINVAL;
+ goto out;
+ dp_idx = nla_get_u16(attrs[DP_GENL_A_DP_IDX]);
+ dp = dp_get(dp_idx);
+ if (!dp) {
+ err = -ENOENT;
+ goto out;
+ }
va = attrs[DP_GENL_A_OPENFLOW];
- if (!va || nla_len(va) != sizeof *state->rq)
- return -EINVAL;
+ len = nla_len(va);
+ if (!va || len < sizeof *rq)
+ goto out;
rq = nla_data(va);
+ type = ntohs(rq->type);
if (rq->header.version != OFP_VERSION
- || rq->header.type != OFPT_FLOW_STATS_REQUEST
- || ntohs(rq->header.length) != sizeof *rq)
- return -EINVAL;
-
- state = kmalloc(sizeof *state, GFP_KERNEL);
- if (!state)
- return -ENOMEM;
- state->dp_idx = nla_get_u32(attrs[DP_GENL_A_DP_IDX]);
- state->table_idx = rq->table_id == 0xff ? 0 : rq->table_id;
- memset(&state->position, 0, sizeof state->position);
- state->rq = rq;
- state->sent_terminator = 0;
-
- cb->args[0] = (long) state;
- } else {
- state = (struct flow_stats_cb_state *) cb->args[0];
- }
-
- if (state->rq->type != OFPFS_INDIV) {
- return -ENOTSUPP;
- }
+ || rq->header.type != OFPT_STATS_REQUEST
+ || ntohs(rq->header.length) != len
+ || type >= ARRAY_SIZE(stats)
+ || !stats[type].dump)
+ goto out;
+
+ s = &stats[type];
+ body_len = len - offsetof(struct ofp_stats_request, body);
+ if (body_len < s->min_body || body_len > s->max_body)
+ goto out;
+
+ cb->args[0] = 1;
+ cb->args[1] = dp_idx;
+ cb->args[2] = type;
+ cb->args[3] = rq->header.xid;
+ if (s->init) {
+ void *state;
+ err = s->init(dp, rq->body, body_len, &state);
+ if (err)
+ goto out;
+ cb->args[4] = (long) state;
+ }
+ } else if (cb->args[0] == 1) {
+ dp_idx = cb->args[1];
+ s = &stats[cb->args[2]];
- rcu_read_lock();
- dp = dp_get(state->dp_idx);
- if (!dp) {
- err = -ENOENT;
+ dp = dp_get(dp_idx);
+ if (!dp) {
+ err = -ENOENT;
+ goto out;
+ }
+ } else {
+ err = 0;
goto out;
}
- sender.xid = state->rq->header.xid;
+ sender.xid = cb->args[3];
sender.pid = NETLINK_CB(cb->skb).pid;
sender.seq = cb->nlh->nlmsg_seq;
- err = muster_flow_stats(dp, state, &sender, skb);
+
+ osr = put_openflow_headers(dp, skb, OFPT_STATS_REPLY, &sender,
+ &max_openflow_len);
+ if (IS_ERR(osr)) {
+ err = PTR_ERR(osr);
+ goto out;
+ }
+ osr->type = htons(s - stats);
+ osr->flags = 0;
+ resize_openflow_skb(skb, &osr->header, max_openflow_len);
+ body = osr->body;
+ body_len = max_openflow_len - offsetof(struct ofp_stats_reply, body);
+
+ err = s->dump(dp, (void *) cb->args[4], body, &body_len);
+ if (err >= 0) {
+ if (!err)
+ cb->args[0] = 2;
+ else
+ osr->flags = ntohs(OFPSF_REPLY_MORE);
+ resize_openflow_skb(skb, &osr->header,
+ (offsetof(struct ofp_stats_reply, body)
+ + body_len));
+ err = skb->len;
+ }
out:
rcu_read_unlock();
static int
dp_genl_openflow_done(struct netlink_callback *cb)
{
- struct flow_stats_cb_state *state;
- state = (struct flow_stats_cb_state *) cb->args[0];
- kfree(state);
+ if (cb->args[0]) {
+ const struct stats_type *s = &stats[cb->args[2]];
+ if (s->done)
+ s->done((void *) cb->args[4]);
+ }
return 0;
}
int dp_send_features_reply(struct datapath *, const struct sender *);
int dp_send_config_reply(struct datapath *, const struct sender *);
int dp_send_flow_expired(struct datapath *, struct sw_flow *);
-int dp_send_flow_stats(struct datapath *, const struct sender *,
- const struct ofp_match *);
-int dp_send_table_stats(struct datapath *, const struct sender *);
-int dp_send_port_stats(struct datapath *, const struct sender *);
int dp_send_error_msg(struct datapath *, const struct sender *,
uint16_t, uint16_t, const uint8_t *, size_t);
int dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp);
}
}
-static int
-recv_port_stats_request(struct sw_chain *chain, const struct sender *sender,
- const void *msg)
-{
- return dp_send_port_stats(chain->dp, sender);
-}
-
-static int
-recv_table_stats_request(struct sw_chain *chain, const struct sender *sender,
- const void *msg)
-{
- return dp_send_table_stats(chain->dp, sender);
-}
-
/* 'msg', which is 'length' bytes long, was received across Netlink from
* 'sender'. Apply it to 'chain'. */
int
sizeof (struct ofp_port_mod),
recv_port_mod,
},
- [OFPT_PORT_STATS_REQUEST] = {
- sizeof (struct ofp_port_stats_request),
- recv_port_stats_request,
- },
- [OFPT_TABLE_STATS_REQUEST] = {
- sizeof (struct ofp_table_stats_request),
- recv_table_stats_request,
- },
};
const struct openflow_packet *pkt;
OFPT_PORT_MOD, /* 10 Controller/switch message */
OFPT_PORT_STATUS, /* 11 Async message */
OFPT_ERROR_MSG, /* 12 Async message */
- OFPT_FLOW_STATS_REQUEST, /* 13 Controller/switch message */
- OFPT_FLOW_STATS_REPLY, /* 14 Controller/switch message */
- OFPT_TABLE_STATS_REQUEST, /* 15 Controller/switch message */
- OFPT_TABLE_STATS_REPLY, /* 16 Controller/switch message */
- OFPT_PORT_STATS_REQUEST, /* 17 Controller/switch message */
- OFPT_PORT_STATS_REPLY /* 18 Controller/switch message */
+ OFPT_STATS_REQUEST, /* 13 Controller/switch message */
+ OFPT_STATS_REPLY /* 14 Controller/switch message */
};
/* Header on all OpenFlow packets. */
on the type and code. */
};
-/* Statistics about flows that match the "match" field */
-struct ofp_flow_stats {
- struct ofp_match match; /* Description of fields */
- uint32_t duration; /* Time flow has been alive in seconds. Only
- used for non-aggregated results. */
- uint64_t packet_count; /* Number of packets in flow. */
- uint64_t byte_count; /* Number of bytes in flow. */
- uint16_t priority; /* Priority of the entry. Only meaningful
- when this is not an exact-match entry. */
- uint8_t table_id; /* ID of table flow came from. */
- uint8_t pad[5]; /* Align to 64-bits. */
+enum ofp_stats_types {
+ /* Flow statistics.
+ * The request body is struct ofp_flow_stats_request.
+ * The reply body is an array of struct ofp_flow_stats. */
+ OFPST_FLOW,
+
+ /* Flow table statistics.
+ * The request body is empty.
+ * The reply body is an array of struct ofp_table_stats. */
+ OFPST_TABLE,
+
+ /* Physical port statistics.
+ * The request body is empty.
+ * The reply body is an array of struct ofp_port_stats. */
+ OFPST_PORT
+};
+
+struct ofp_stats_request {
+ struct ofp_header header;
+ uint16_t type; /* One of the OFPST_* constants. */
+ uint16_t flags; /* OFPSF_REQ_* flags (none yet defined). */
+ uint8_t body[0]; /* Body of the request. */
+};
+
+enum ofp_stats_reply_flags {
+ OFPSF_REPLY_MORE = 1 << 0, /* More replies to follow */
+};
+
+struct ofp_stats_reply {
+ struct ofp_header header;
+ uint16_t type; /* One of the OFPST_* constants. */
+ uint16_t flags; /* OFPSF_REPLY_* flags. */
+ uint8_t body[0]; /* Body of the reply. */
};
enum ofp_stats_type {
OFPFS_AGGREGATE /* Aggregate matching flows */
};
-/* Current flow statistics request */
+/* Body for ofp_stats_request of type OFPST_FLOW. */
struct ofp_flow_stats_request {
- struct ofp_header header;
struct ofp_match match; /* Fields to match */
uint8_t table_id; /* ID of table to read (from ofp_table_stats)
or 0xff for all tables. */
uint16_t pad; /* Align to 32-bits */
};
-/* Current flow statistics reply */
-struct ofp_flow_stats_reply {
- struct ofp_header header;
-
- /* If request was of type OFPFS_INDIV, this will contain an array of
- * flow statistic entries. The number of matching flows is likely
- * much larger than can fit in a single OpenFlow message, so a
- * a response with no flows included is sent to indicate the end.
- * If it was a OFPFS_AGGREGATE request, only a single flow stats
- * entry will be contained in the response.
- */
- struct ofp_flow_stats flows[0];
-};
-
-/* Current table statistics request */
-struct ofp_table_stats_request {
- struct ofp_header header;
+/* Body of reply to OFPST_FLOW request. */
+struct ofp_flow_stats {
+ struct ofp_match match; /* Description of fields */
+ uint32_t duration; /* Time flow has been alive in seconds. Only
+ used for non-aggregated results. */
+ uint64_t packet_count; /* Number of packets in flow. */
+ uint64_t byte_count; /* Number of bytes in flow. */
+ uint16_t priority; /* Priority of the entry. Only meaningful
+ when this is not an exact-match entry. */
+ uint8_t table_id; /* ID of table flow came from. */
+ uint8_t pad[5]; /* Align to 64-bits. */
};
-/* Statistics about a particular table */
+/* Body of reply to OFPST_TABLE request. */
struct ofp_table_stats {
uint8_t table_id;
uint8_t pad[3]; /* Align to 32-bits */
uint64_t matched_count; /* Number of packets that hit table */
};
-/* Current table statistics reply */
-struct ofp_table_stats_reply {
- struct ofp_header header;
- struct ofp_table_stats tables[0]; /* The number of entries is inferred from
- the length field in the header. */
-};
-
/* Statistics about a particular port */
struct ofp_port_stats {
uint16_t port_no;
uint64_t drop_count; /* Number of packets dropped by interface */
};
-/* Current port statistics request */
-struct ofp_port_stats_request {
- struct ofp_header header;
-};
-
-/* Current port statistics reply */
-struct ofp_port_stats_reply {
- struct ofp_header header;
- struct ofp_port_stats ports[0]; /* The number of entries is inferred from
- the length field in the header. */
-};
-
#endif /* openflow.h */
struct buffer *rconn_recv(struct rconn *);
void rconn_recv_wait(struct rconn *);
int rconn_send(struct rconn *, struct buffer *);
+bool rconn_is_full(const struct rconn *);
const char *rconn_get_name(const struct rconn *);
bool rconn_is_alive(const struct rconn *);
int n_iov;
int retval;
- /* The reply to OFPT_FLOW_STATS_REQUEST may be multiple segments long, so
- * we need to specify NLM_F_DUMP in the request. */
+ /* The reply to OFPT_STATS_REQUEST may be multiple segments long, so we
+ * need to specify NLM_F_DUMP in the request. */
oh = buffer_at_assert(buffer, 0, sizeof *oh);
- dump_flag = oh->type == OFPT_FLOW_STATS_REQUEST ? NLM_F_DUMP : 0;
+ dump_flag = oh->type == OFPT_STATS_REQUEST ? NLM_F_DUMP : 0;
buffer_use(&hdr, fixed_buffer, sizeof fixed_buffer);
nl_msg_put_genlmsghdr(&hdr, dp->sock, 32, openflow_family,
}
static void
-ofp_flow_stats_reply(struct ds *string, const void *oh, size_t len,
+ofp_flow_stats_reply(struct ds *string, const void *body, size_t len,
int verbosity)
{
- const struct ofp_flow_stats_reply *fsr = oh;
- const struct ofp_flow_stats *fs;
- size_t n;
-
- n = (len - offsetof(struct ofp_flow_stats_reply, flows)) / sizeof *fs;
+ const struct ofp_flow_stats *fs = body;
+ size_t n = len / sizeof *fs;
ds_put_format(string, " %zu flows\n", n);
if (verbosity < 1) {
return;
}
- for (fs = &fsr->flows[0]; fs < &fsr->flows[n]; fs++) {
+ for (; n--; fs++) {
ds_put_format(string, " duration=%"PRIu32" s, ", ntohl(fs->duration));
ds_put_format(string, "table_id=%"PRIu8", ", fs->table_id);
ds_put_format(string, "priority=%"PRIu16", ",
}
static void
-ofp_port_stats_reply(struct ds *string, const void *oh, size_t len,
- int verbosity)
+ofp_port_stats_reply(struct ds *string, const void *body, size_t len,
+ int verbosity)
{
- const struct ofp_port_stats_reply *psr = oh;
- const struct ofp_port_stats *ps;
- size_t n;
-
- n = (len - offsetof(struct ofp_port_stats_reply, ports)) / sizeof *ps;
+ const struct ofp_port_stats *ps = body;
+ size_t n = len / sizeof *ps;
ds_put_format(string, " %zu ports\n", n);
if (verbosity < 1) {
return;
}
- for (ps = &psr->ports[0]; ps < &psr->ports[n]; ps++) {
+ for (; n--; ps++) {
ds_put_format(string, " port %"PRIu16": ", ntohs(ps->port_no));
ds_put_format(string, "rx %"PRIu64", ", ntohll(ps->rx_count));
ds_put_format(string, "tx %"PRIu64", ", ntohll(ps->tx_count));
}
static void
-ofp_table_stats_reply(struct ds *string, const void *oh, size_t len,
+ofp_table_stats_reply(struct ds *string, const void *body, size_t len,
int verbosity)
{
- const struct ofp_table_stats_reply *tsr = oh;
- const struct ofp_table_stats *ts;
- size_t n;
-
- n = (len - offsetof(struct ofp_table_stats_reply, tables)) / sizeof *ts;
+ const struct ofp_table_stats *ts = body;
+ size_t n = len / sizeof *ts;
ds_put_format(string, " %zu tables\n", n);
if (verbosity < 1) {
return;
}
- for (ts = &tsr->tables[0]; ts < &tsr->tables[n]; ts++) {
+ for (; n--; ts++) {
char name[OFP_MAX_TABLE_NAME_LEN + 1];
strncpy(name, ts->name, sizeof name);
name[OFP_MAX_TABLE_NAME_LEN] = '\0';
}
}
+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 {
+ const char *name;
+ struct stats_msg request;
+ struct stats_msg reply;
+ };
+
+ static const struct stats_type stats_types[] = {
+ [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_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 },
+ },
+ };
+
+ 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;
+ }
+ s = &stats_types[type];
+ ds_put_format(string, " type=%d(%s)", 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 %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%04"PRIx16"***]", flags);
+ }
+ }
+
+ print_stats(string, ntohs(srp->type), srp->body,
+ len - offsetof(struct ofp_stats_reply, body),
+ verbosity, REPLY);
+}
+
struct openflow_packet {
const char *name;
size_t min_size;
sizeof (struct ofp_error_msg),
ofp_print_error_msg,
},
- [OFPT_FLOW_STATS_REQUEST] = {
- "flow_stats_request",
- sizeof (struct ofp_flow_stats_request),
- ofp_flow_stats_request,
- },
- [OFPT_FLOW_STATS_REPLY] = {
- "flow_stats_reply",
- sizeof (struct ofp_flow_stats_reply),
- ofp_flow_stats_reply,
- },
- [OFPT_PORT_STATS_REQUEST] = {
- "port_stats_request",
- sizeof (struct ofp_port_stats_request),
- NULL,
- },
- [OFPT_PORT_STATS_REPLY] = {
- "port_stats_reply",
- sizeof (struct ofp_port_stats_reply),
- ofp_port_stats_reply,
+ [OFPT_STATS_REQUEST] = {
+ "stats_request",
+ sizeof (struct ofp_stats_request),
+ ofp_stats_request,
},
- [OFPT_TABLE_STATS_REQUEST] = {
- "table_stats_request",
- sizeof (struct ofp_table_stats_request),
- NULL,
- },
- [OFPT_TABLE_STATS_REPLY] = {
- "table_stats_reply",
- sizeof (struct ofp_table_stats_reply),
- ofp_table_stats_reply,
+ [OFPT_STATS_REPLY] = {
+ "stats_reply",
+ sizeof (struct ofp_stats_reply),
+ ofp_stats_reply,
},
};
if (verbosity >= 3) {
ds_put_hex_dump(&string, oh, len, 0, true);
}
+ if (string.string[string.length - 1] != '\n') {
+ ds_put_char(&string, '\n');
+ }
return ds_cstr(&string);
}
\f
}
}
+/* Returns true if 'rc''s send buffer is full,
+ * false if it has room for at least one more packet. */
+bool
+rconn_is_full(const struct rconn *rc)
+{
+ return rc->txq.n >= rc->txq_limit;
+}
+
/* Returns 'rc''s name (the 'name' argument passed to rconn_new()). */
const char *
rconn_get_name(const struct rconn *rc)
switch-flow.h \
table.h \
table-hash.c \
- table-linear.c \
- table-mac.c
+ table-linear.c
switch_LDADD = ../lib/libopenflow.la -ldl
if (chain == NULL)
return NULL;
- if (add_table(chain, table_mac_create(TABLE_MAC_NUM_BUCKETS,
- TABLE_MAC_MAX_FLOWS))
- || add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS,
+ if (add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS,
0x741B8CD7, TABLE_HASH_MAX_FLOWS))
|| add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS))) {
chain_destroy(chain);
unsigned long long int rx_count, tx_count, drop_count;
};
+/* The origin of a received OpenFlow message, to enable sending a reply. */
+struct sender {
+ struct remote *remote; /* The device that sent the message. */
+ uint32_t xid; /* The OpenFlow transaction ID. */
+};
+
/* A connection to a controller or a management device. */
struct remote {
struct list node;
struct rconn *rconn;
-};
-/* The origin of a received OpenFlow message, to enable sending a reply. */
-struct sender {
- struct remote *remote; /* The device that sent the message. */
- uint32_t xid; /* The OpenFlow transaction ID. */
+ /* Support for reliable, multi-message replies to requests.
+ *
+ * If an incoming request needs to have a reliable reply that might
+ * require multiple messages, it can use remote_start_dump() to set up
+ * a callback that will be called as buffer space for replies. */
+ int (*cb_dump)(struct datapath *, void *aux);
+ void (*cb_done)(void *aux);
+ void *cb_aux;
};
struct datapath {
}
void
-dp_run(struct datapath *dp)
+dp_run(struct datapath *dp)
{
time_t now = time(0);
struct sw_port *p, *pn;
rconn_run(r->rconn);
- /* Process a number of commands from the remote, but cap them at a
- * reasonable number so that other processing doesn't starve. */
+ /* Do some remote processing, but cap it at a reasonable amount so that
+ * other processing doesn't starve. */
for (i = 0; i < 50; i++) {
- struct buffer *buffer;
- struct ofp_header *oh;
+ if (!r->cb_dump) {
+ struct buffer *buffer;
+ struct ofp_header *oh;
- buffer = rconn_recv(r->rconn);
- if (!buffer) {
- break;
- }
+ buffer = rconn_recv(r->rconn);
+ if (!buffer) {
+ break;
+ }
- if (buffer->size >= sizeof *oh) {
- struct sender sender;
+ if (buffer->size >= sizeof *oh) {
+ struct sender sender;
- oh = buffer->data;
- sender.remote = r;
- sender.xid = oh->xid;
- fwd_control_input(dp, &sender, buffer->data, buffer->size);
+ oh = buffer->data;
+ sender.remote = r;
+ sender.xid = oh->xid;
+ fwd_control_input(dp, &sender, buffer->data, buffer->size);
+ } else {
+ VLOG_WARN("received too-short OpenFlow message");
+ }
+ buffer_delete(buffer);
} else {
- VLOG_WARN("received too-short OpenFlow message");
+ if (!rconn_is_full(r->rconn)) {
+ int error = r->cb_dump(dp, r->cb_aux);
+ if (error <= 0) {
+ if (error) {
+ VLOG_WARN("dump callback error: %s", strerror(-error));
+ }
+ r->cb_done(r->cb_aux);
+ r->cb_dump = NULL;
+ }
+ } else {
+ break;
+ }
}
- buffer_delete(buffer);
}
if (!rconn_is_alive(r->rconn)) {
remote_destroy(struct remote *r)
{
if (r) {
+ if (r->cb_dump && r->cb_done) {
+ r->cb_done(r->cb_aux);
+ }
list_remove(&r->node);
rconn_destroy(r->rconn);
free(r);
struct remote *remote = xmalloc(sizeof *remote);
list_push_back(&dp->remotes, &remote->node);
remote->rconn = rconn;
+ remote->cb_dump = NULL;
return remote;
}
+/* Starts a callback-based, reliable, possibly multi-message reply to a
+ * request made by 'remote'.
+ *
+ * 'dump' designates a function that will be called when the 'remote' send
+ * queue has an empty slot. It should compose a message and send it on
+ * 'remote'. On success, it should return 1 if it should be called again when
+ * another send queue slot opens up, 0 if its transmissions are complete, or a
+ * negative errno value on failure.
+ *
+ * 'done' designates a function to clean up any resources allocated for the
+ * dump. It must handle being called before the dump is complete (which will
+ * happen if 'remote' is closed unexpectedly).
+ *
+ * 'aux' is passed to 'dump' and 'done'. */
+static void
+remote_start_dump(struct remote *remote,
+ int (*dump)(struct datapath *, void *),
+ void (*done)(void *),
+ void *aux)
+{
+ assert(!remote->cb_dump);
+ remote->cb_dump = dump;
+ remote->cb_done = done;
+ remote->cb_aux = aux;
+}
+
void
dp_wait(struct datapath *dp)
{
memset(ofs->pad, 0, sizeof ofs->pad);
}
-int
-dp_send_flow_stats(struct datapath *dp, const struct sender *sender,
- const struct ofp_match *match)
-{
- struct buffer *buffer;
- struct ofp_flow_stats_reply *fsr;
- size_t header_size, fudge, flow_size;
- struct sw_flow_key match_key;
- int table_idx, n_flows, max_flows;
- time_t now;
-
- header_size = offsetof(struct ofp_flow_stats_reply, flows);
- fudge = 128;
- flow_size = sizeof fsr->flows[0];
- max_flows = (65536 - header_size - fudge) / flow_size;
- fsr = alloc_openflow_buffer(dp, header_size,
- OFPT_FLOW_STATS_REPLY, sender, &buffer);
-
- n_flows = 0;
- flow_extract_match(&match_key, match);
- now = time(0);
- for (table_idx = 0; table_idx < dp->chain->n_tables; table_idx++) {
- struct sw_table *table = dp->chain->tables[table_idx];
- struct swt_iterator iter;
-
- if (n_flows >= max_flows) {
- break;
- }
-
- if (!table->iterator(table, &iter)) {
- printf("iterator failed for table %d\n", table_idx);
- continue;
- }
-
- for (; iter.flow; table->iterator_next(&iter)) {
- if (flow_matches(&match_key, &iter.flow->key)) {
- struct ofp_flow_stats *ofs = buffer_put_uninit(buffer,
- sizeof *ofs);
- fill_flow_stats(ofs, iter.flow, table_idx, now);
- if (++n_flows >= max_flows) {
- break;
- }
- }
- }
- table->iterator_destroy(&iter);
- }
- return send_openflow_buffer(dp, buffer, sender);
-}
-
-int
-dp_send_port_stats(struct datapath *dp, const struct sender *sender)
-{
- struct buffer *buffer;
- struct ofp_port_stats_reply *psr;
- struct sw_port *p;
-
- psr = alloc_openflow_buffer(dp, offsetof(struct ofp_port_stats_reply,
- ports),
- OFPT_PORT_STATS_REPLY, sender, &buffer);
- LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
- struct ofp_port_stats *ps = buffer_put_uninit(buffer, sizeof *ps);
- ps->port_no = htons(port_no(dp, p));
- memset(ps->pad, 0, sizeof ps->pad);
- ps->rx_count = htonll(p->rx_count);
- ps->tx_count = htonll(p->tx_count);
- ps->drop_count = htonll(p->drop_count);
- }
- return send_openflow_buffer(dp, buffer, sender);
-}
-
-int
-dp_send_table_stats(struct datapath *dp, const struct sender *sender)
-{
- struct buffer *buffer;
- struct ofp_table_stats_reply *tsr;
- int i;
-
- tsr = alloc_openflow_buffer(dp, offsetof(struct ofp_table_stats_reply,
- tables),
- OFPT_TABLE_STATS_REPLY, sender, &buffer);
- for (i = 0; i < dp->chain->n_tables; i++) {
- struct ofp_table_stats *ots = buffer_put_uninit(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->pad[0] = ots->pad[1] = 0;
- ots->max_entries = htonl(stats.max_flows);
- ots->active_count = htonl(stats.n_flows);
- ots->matched_count = htonll(0); /* FIXME */
- }
- return send_openflow_buffer(dp, buffer, sender);
-}
\f
/* 'buffer' was received on 'in_port', a physical switch port between 0 and
* OFPP_MAX. Process it according to 'chain'. */
}
}
-static int
-recv_flow_stats_request(struct datapath *dp, const struct sender *sender,
- const void *msg)
+struct flow_stats_state {
+ int table_idx;
+ struct sw_table_position position;
+ struct ofp_flow_stats_request rq;
+ time_t now;
+
+ struct buffer *buffer;
+ int n_flows, max_flows;
+};
+
+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;
+ struct ofp_flow_stats *ofs = buffer_put_uninit(s->buffer, sizeof *ofs);
+ fill_flow_stats(ofs, flow, s->table_idx, s->now);
+ return ++s->n_flows >= s->max_flows;
+}
+
+static int flow_stats_dump(struct datapath *dp, void *state,
+ struct buffer *buffer)
+{
+ struct flow_stats_state *s = state;
+ struct ofp_flow_stats *ofs;
+ struct sw_flow_key match_key;
+
+ s->max_flows = 4096 / sizeof *ofs;
+ if (!s->max_flows)
+ return -ENOMEM;
+
+ flow_extract_match(&match_key, &s->rq.match);
+ s->buffer = buffer;
+ s->n_flows = 0;
+ s->now = time(0);
+ 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->position,
+ flow_stats_dump_callback, s))
+ break;
+
+ s->table_idx++;
+ memset(&s->position, 0, sizeof s->position);
+ }
+ return s->n_flows >= s->max_flows;
+}
+
+static void flow_stats_done(void *state)
+{
+ free(state);
+}
+
+static int table_stats_dump(struct datapath *dp, void *state,
+ struct buffer *buffer)
+{
+ int i;
+ for (i = 0; i < dp->chain->n_tables; i++) {
+ struct ofp_table_stats *ots = buffer_put_uninit(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;
+ memset(ots->pad, 0, sizeof ots->pad);
+ ots->max_entries = htonl(stats.max_flows);
+ ots->active_count = htonl(stats.n_flows);
+ ots->matched_count = htonll(0); /* FIXME */
+ }
+ return 0;
+}
+
+struct port_stats_state {
+ int port;
+};
+
+static int port_stats_init(struct datapath *dp, const void *body, int body_len,
+ void **state)
{
- const struct ofp_flow_stats_request *fsr = msg;
- if (fsr->type == OFPFS_INDIV) {
- return dp_send_flow_stats(dp, sender, &fsr->match);
- } else {
- /* FIXME */
- return -ENOSYS;
+ 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 buffer *buffer)
+{
+ struct port_stats_state *s = state;
+ int i;
+
+ for (i = s->port; i < OFPP_MAX; i++) {
+ struct sw_port *p = &dp->ports[i];
+ struct ofp_port_stats *ops;
+ if (!p->netdev) {
+ continue;
+ }
+ ops = buffer_put_uninit(buffer, sizeof *ops);
+ ops->port_no = htons(port_no(dp, p));
+ memset(ops->pad, 0, sizeof ops->pad);
+ ops->rx_count = htonll(p->rx_count);
+ ops->tx_count = htonll(p->tx_count);
+ ops->drop_count = htonll(p->drop_count);
+ ops++;
}
+ s->port = i;
+ return 0;
+}
+
+static void port_stats_done(void *state)
+{
+ free(state);
}
+struct stats_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 buffer *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_FLOW] = {
+ sizeof(struct ofp_flow_stats_request),
+ sizeof(struct ofp_flow_stats_request),
+ flow_stats_init,
+ flow_stats_dump,
+ flow_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
-recv_port_stats_request(struct datapath *dp, const struct sender *sender,
- const void *msg)
+stats_dump(struct datapath *dp, void *cb_)
+{
+ struct stats_dump_cb *cb = cb_;
+ struct ofp_stats_reply *osr;
+ struct buffer *buffer;
+ int err;
+
+ if (cb->done) {
+ return 0;
+ }
+
+ osr = alloc_openflow_buffer(dp, sizeof *osr, OFPT_STATS_REPLY, &cb->sender,
+ &buffer);
+ osr->type = htons(cb->s - stats);
+ 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 = buffer_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_)
{
- return dp_send_port_stats(dp, sender);
+ struct stats_dump_cb *cb = cb_;
+ if (cb) {
+ if (cb->s->done) {
+ cb->s->done(cb->state);
+ }
+ free(cb);
+ }
}
static int
-recv_table_stats_request(struct datapath *dp, const struct sender *sender,
- const void *msg)
+recv_stats_request(struct datapath *dp, const struct sender *sender,
+ const void *oh)
{
- return dp_send_table_stats(dp, sender);
+ const struct ofp_stats_request *rq = oh;
+ size_t rq_len = ntohs(rq->header.length);
+ struct stats_dump_cb *cb;
+ int type, body_len;
+ int err;
+
+ type = ntohs(rq->type);
+ if (type >= ARRAY_SIZE(stats) || !stats[type].dump) {
+ VLOG_WARN("received stats request of unknown type %d", type);
+ return -EINVAL;
+ }
+
+ cb = xmalloc(sizeof *cb);
+ cb->done = false;
+ cb->rq = xmemdup(rq, rq_len);
+ cb->sender = *sender;
+ cb->s = &stats[type];
+ 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("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("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;
}
/* 'msg', which is 'length' bytes long, was received from the control path.
sizeof (struct ofp_port_mod),
recv_port_mod,
},
- [OFPT_FLOW_STATS_REQUEST] = {
- sizeof (struct ofp_flow_stats_request),
- recv_flow_stats_request,
- },
- [OFPT_PORT_STATS_REQUEST] = {
- sizeof (struct ofp_port_stats_request),
- recv_port_stats_request,
- },
- [OFPT_TABLE_STATS_REQUEST] = {
- sizeof (struct ofp_table_stats_request),
- recv_table_stats_request,
+ [OFPT_STATS_REQUEST] = {
+ sizeof (struct ofp_stats_request),
+ recv_stats_request,
},
};
time_t timeout; /* When the flow expires (if idle). */
uint64_t packet_count; /* Number of packets seen. */
uint64_t byte_count; /* Number of bytes seen. */
+
+ /* Private to table implementations. */
struct list node;
+ struct list iter_node;
+ unsigned long int serial;
/* Actions (XXX probably most flows have only a single action). */
unsigned int n_actions;
free(th);
}
-struct swt_iterator_hash {
- struct sw_table_hash *th;
- unsigned int bucket_i;
-};
-
-static struct sw_flow *next_flow(struct swt_iterator_hash *ih)
-{
- for (;ih->bucket_i <= ih->th->bucket_mask; ih->bucket_i++) {
- struct sw_flow *f = ih->th->buckets[ih->bucket_i];
- if (f != NULL)
- return f;
- }
-
- return NULL;
-}
-
-static int table_hash_iterator(struct sw_table *swt,
- struct swt_iterator *swt_iter)
+static int table_hash_iterate(struct sw_table *swt,
+ const struct sw_flow_key *key,
+ struct sw_table_position *position,
+ int (*callback)(struct sw_flow *, void *private),
+ void *private)
{
- struct swt_iterator_hash *ih;
-
- swt_iter->private = ih = malloc(sizeof *ih);
+ struct sw_table_hash *th = (struct sw_table_hash *) swt;
- if (ih == NULL)
+ if (position->private[0] > th->bucket_mask)
return 0;
- ih->th = (struct sw_table_hash *) swt;
-
- ih->bucket_i = 0;
- swt_iter->flow = next_flow(ih);
-
- return 1;
-}
-
-static void table_hash_next(struct swt_iterator *swt_iter)
-{
- struct swt_iterator_hash *ih;
-
- if (swt_iter->flow == NULL)
- return;
-
- ih = (struct swt_iterator_hash *) swt_iter->private;
-
- ih->bucket_i++;
- swt_iter->flow = next_flow(ih);
-}
-
-static void table_hash_iterator_destroy(struct swt_iterator *swt_iter)
-{
- free(swt_iter->private);
+ if (key->wildcards == 0) {
+ struct sw_flow *flow = table_hash_lookup(swt, key);
+ position->private[0] = -1;
+ return flow ? callback(flow, private) : 0;
+ } else {
+ int i;
+
+ for (i = position->private[0]; i <= th->bucket_mask; i++) {
+ struct sw_flow *flow = th->buckets[i];
+ if (flow && flow_matches(key, &flow->key)) {
+ int error = callback(flow, private);
+ if (error) {
+ position->private[0] = i + 1;
+ return error;
+ }
+ }
+ }
+ return 0;
+ }
}
static void table_hash_stats(struct sw_table *swt,
swt->delete = table_hash_delete;
swt->timeout = table_hash_timeout;
swt->destroy = table_hash_destroy;
- swt->iterator = table_hash_iterator;
- swt->iterator_next = table_hash_next;
- swt->iterator_destroy = table_hash_iterator_destroy;
+ swt->iterate = table_hash_iterate;
swt->stats = table_hash_stats;
crc32_init(&th->crc32, polynomial);
free(t2);
}
-struct swt_iterator_hash2 {
- struct sw_table_hash2 *th2;
- struct swt_iterator ih;
- uint8_t table_i;
-};
-
-static int table_hash2_iterator(struct sw_table *swt,
- struct swt_iterator *swt_iter)
+static int table_hash2_iterate(struct sw_table *swt,
+ const struct sw_flow_key *key,
+ struct sw_table_position *position,
+ int (*callback)(struct sw_flow *, void *),
+ void *private)
{
- struct swt_iterator_hash2 *ih2;
-
- swt_iter->private = ih2 = malloc(sizeof *ih2);
- if (ih2 == NULL)
- return 0;
-
- ih2->th2 = (struct sw_table_hash2 *) swt;
- if (!table_hash_iterator(ih2->th2->subtable[0], &ih2->ih)) {
- free(ih2);
- return 0;
- }
-
- if (ih2->ih.flow != NULL) {
- swt_iter->flow = ih2->ih.flow;
- ih2->table_i = 0;
- } else {
- table_hash_iterator_destroy(&ih2->ih);
- ih2->table_i = 1;
- if (!table_hash_iterator(ih2->th2->subtable[1], &ih2->ih)) {
- free(ih2);
- return 0;
- }
- swt_iter->flow = ih2->ih.flow;
- }
-
- return 1;
-}
-
-static void table_hash2_next(struct swt_iterator *swt_iter)
-{
- struct swt_iterator_hash2 *ih2;
-
- if (swt_iter->flow == NULL)
- return;
-
- ih2 = (struct swt_iterator_hash2 *) swt_iter->private;
- table_hash_next(&ih2->ih);
+ struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt;
+ int i;
- if (ih2->ih.flow != NULL) {
- swt_iter->flow = ih2->ih.flow;
- } else {
- if (ih2->table_i == 0) {
- table_hash_iterator_destroy(&ih2->ih);
- ih2->table_i = 1;
- if (!table_hash_iterator(ih2->th2->subtable[1], &ih2->ih)) {
- ih2->ih.private = NULL;
- swt_iter->flow = NULL;
- } else {
- swt_iter->flow = ih2->ih.flow;
- }
- } else {
- swt_iter->flow = NULL;
+ for (i = position->private[1]; i < 2; i++) {
+ int error = table_hash_iterate(t2->subtable[i], key, position,
+ callback, private);
+ if (error) {
+ return error;
}
+ position->private[0] = 0;
+ position->private[1]++;
}
-}
-
-static void table_hash2_iterator_destroy(struct swt_iterator *swt_iter)
-{
- struct swt_iterator_hash2 *ih2;
-
- ih2 = (struct swt_iterator_hash2 *) swt_iter->private;
- if (ih2->ih.private != NULL)
- table_hash_iterator_destroy(&ih2->ih);
- free(ih2);
+ return 0;
}
static void table_hash2_stats(struct sw_table *swt,
swt->delete = table_hash2_delete;
swt->timeout = table_hash2_timeout;
swt->destroy = table_hash2_destroy;
+ swt->iterate = table_hash2_iterate;
swt->stats = table_hash2_stats;
- swt->iterator = table_hash2_iterator;
- swt->iterator_next = table_hash2_next;
- swt->iterator_destroy = table_hash2_iterator_destroy;
-
return swt;
out_free_subtable0:
unsigned int max_flows;
unsigned int n_flows;
struct list flows;
+ struct list iter_flows;
+ unsigned long int next_serial;
};
static struct sw_flow *table_linear_lookup(struct sw_table *swt,
if (f->priority == flow->priority
&& f->key.wildcards == flow->key.wildcards
&& flow_matches(&f->key, &flow->key)) {
+ flow->serial = f->serial;
list_replace(&flow->node, &f->node);
+ list_replace(&flow->iter_node, &f->iter_node);
flow_free(f);
return 1;
}
tl->n_flows++;
/* Insert the entry immediately in front of where we're pointing. */
- list_push_back(&f->node, &flow->node);
+ list_insert(&f->node, &flow->node);
+ list_push_front(&tl->iter_flows, &flow->iter_node);
return 1;
}
do_delete(struct sw_flow *flow)
{
list_remove(&flow->node);
+ list_remove(&flow->iter_node);
flow_free(flow);
}
LIST_FOR_EACH_SAFE (flow, n, struct sw_flow, node, &tl->flows) {
if (flow_timeout(flow)) {
list_remove(&flow->node);
+ list_remove(&flow->iter_node);
list_push_back(deleted, &flow->node);
tl->n_flows--;
}
free(tl);
}
-/* Linear table's private data is just a pointer to the table */
-
-static int table_linear_iterator(struct sw_table *swt,
- struct swt_iterator *swt_iter)
+static int table_linear_iterate(struct sw_table *swt,
+ const struct sw_flow_key *key,
+ struct sw_table_position *position,
+ int (*callback)(struct sw_flow *, void *),
+ void *private)
{
struct sw_table_linear *tl = (struct sw_table_linear *) swt;
-
- swt_iter->private = tl;
-
- if (!tl->n_flows)
- swt_iter->flow = NULL;
- else
- swt_iter->flow = CONTAINER_OF(list_front(&tl->flows), struct sw_flow, node);
-
- return 1;
-}
-
-static void table_linear_next(struct swt_iterator *swt_iter)
-{
- struct sw_table_linear *tl;
- struct list *next;
-
- if (swt_iter->flow == NULL)
- return;
-
- tl = (struct sw_table_linear *) swt_iter->private;
-
- next = swt_iter->flow->node.next;
- if (next == &tl->flows)
- swt_iter->flow = NULL;
- else
- swt_iter->flow = CONTAINER_OF(next, struct sw_flow, node);
+ struct sw_flow *flow;
+ unsigned long start;
+
+ start = ~position->private[0];
+ LIST_FOR_EACH (flow, struct sw_flow, iter_node, &tl->iter_flows) {
+ if (flow->serial <= start && flow_matches(key, &flow->key)) {
+ int error = callback(flow, private);
+ if (error) {
+ position->private[0] = ~(flow->serial - 1);
+ return error;
+ }
+ }
+ }
+ return 0;
}
-static void table_linear_iterator_destroy(struct swt_iterator *swt_iter)
-{}
-
static void table_linear_stats(struct sw_table *swt,
struct sw_table_stats *stats)
{
swt->delete = table_linear_delete;
swt->timeout = table_linear_timeout;
swt->destroy = table_linear_destroy;
+ swt->iterate = table_linear_iterate;
swt->stats = table_linear_stats;
- swt->iterator = table_linear_iterator;
- swt->iterator_next = table_linear_next;
- swt->iterator_destroy = table_linear_iterator_destroy;
-
tl->max_flows = max_flows;
tl->n_flows = 0;
list_init(&tl->flows);
+ list_init(&tl->iter_flows);
+ tl->next_serial = 0;
return swt;
}
+++ /dev/null
-/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
- * Junior University
- *
- * We are making the OpenFlow specification and associated documentation
- * (Software) available for public use and benefit with the expectation
- * that others will use, modify and enhance the Software and contribute
- * those enhancements back to the community. However, since we would
- * like to make the Software available for broadest use, with as few
- * restrictions as possible permission is hereby granted, free of
- * charge, to any person obtaining a copy of this Software to deal in
- * the Software under the copyrights without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * The name and trademarks of copyright holder(s) may NOT be used in
- * advertising or publicity pertaining to the Software or any
- * derivatives without specific, written prior permission.
- */
-
-#include "table.h"
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include "crc32.h"
-#include "switch-flow.h"
-#include "openflow.h"
-#include "datapath.h"
-
-struct sw_table_mac {
- struct sw_table swt;
- struct crc32 crc32;
- unsigned int n_flows;
- unsigned int max_flows;
- unsigned int bucket_mask; /* Number of buckets minus 1. */
- struct list *buckets;
-};
-
-static struct list *find_bucket(struct sw_table *swt,
- const struct sw_flow_key *key)
-{
- struct sw_table_mac *tm = (struct sw_table_mac *) swt;
- unsigned int crc = crc32_calculate(&tm->crc32, key, sizeof *key);
- return &tm->buckets[crc & tm->bucket_mask];
-}
-
-static struct sw_flow *table_mac_lookup(struct sw_table *swt,
- const struct sw_flow_key *key)
-{
- struct list *bucket = find_bucket(swt, key);
- struct sw_flow *flow;
- LIST_FOR_EACH (flow, struct sw_flow, node, bucket) {
- if (!memcmp(key->flow.dl_src, flow->key.flow.dl_src, 6)) {
- return flow;
- }
- }
- return NULL;
-}
-
-static int table_mac_insert(struct sw_table *swt, struct sw_flow *flow)
-{
- struct sw_table_mac *tm = (struct sw_table_mac *) swt;
- struct list *bucket;
- struct sw_flow *f;
-
- /* MAC table only handles flows that match on Ethernet
- source address and wildcard everything else. */
- if (flow->key.wildcards != (OFPFW_ALL & ~OFPFW_DL_SRC))
- return 0;
- bucket = find_bucket(swt, &flow->key);
-
- LIST_FOR_EACH (f, struct sw_flow, node, bucket) {
- if (!memcmp(f->key.flow.dl_src, flow->key.flow.dl_src, 6)) {
- list_replace(&flow->node, &f->node);
- flow_free(f);
- return 1;
- }
- }
-
- /* Table overflow? */
- if (tm->n_flows >= tm->max_flows) {
- return 0;
- }
- tm->n_flows++;
-
- list_push_front(bucket, &flow->node);
- return 1;
-}
-
-static void
-do_delete(struct sw_flow *flow)
-{
- list_remove(&flow->node);
- flow_free(flow);
-}
-
-/* Returns number of deleted flows. */
-static int table_mac_delete(struct sw_table *swt,
- const struct sw_flow_key *key, int strict)
-{
- struct sw_table_mac *tm = (struct sw_table_mac *) swt;
-
- if (key->wildcards == (OFPFW_ALL & ~OFPFW_DL_SRC)) {
- struct sw_flow *flow = table_mac_lookup(swt, key);
- if (flow) {
- do_delete(flow);
- tm->n_flows--;
- return 1;
- }
- return 0;
- } else {
- unsigned int i;
- int count = 0;
- for (i = 0; i <= tm->bucket_mask; i++) {
- struct list *bucket = &tm->buckets[i];
- struct sw_flow *flow, *next;
- LIST_FOR_EACH_SAFE (flow, next, struct sw_flow, node, bucket) {
- if (flow_del_matches(&flow->key, key, strict)) {
- do_delete(flow);
- count++;
- }
- }
- }
- tm->n_flows -= count;
- return count;
- }
-}
-
-static void table_mac_timeout(struct sw_table *swt, struct list *deleted)
-{
- struct sw_table_mac *tm = (struct sw_table_mac *) swt;
- unsigned int i;
-
- for (i = 0; i <= tm->bucket_mask; i++) {
- struct list *bucket = &tm->buckets[i];
- struct sw_flow *flow, *next;
- LIST_FOR_EACH_SAFE (flow, next, struct sw_flow, node, bucket) {
- if (flow_timeout(flow)) {
- list_remove(&flow->node);
- list_push_back(deleted, &flow->node);
- tm->n_flows--;
- }
- }
- }
-}
-
-static void table_mac_destroy(struct sw_table *swt)
-{
- struct sw_table_mac *tm = (struct sw_table_mac *) swt;
- unsigned int i;
- for (i = 0; i <= tm->bucket_mask; i++) {
- struct list *list = &tm->buckets[i];
- while (!list_is_empty(list)) {
- struct sw_flow *flow = CONTAINER_OF(list_front(list),
- struct sw_flow, node);
- list_remove(&flow->node);
- flow_free(flow);
- }
- }
- free(tm->buckets);
- free(tm);
-}
-
-struct swt_iterator_mac {
- struct sw_table_mac *tm;
- unsigned int bucket_i;
-};
-
-static struct sw_flow *next_head_flow(struct swt_iterator_mac *im)
-{
- for (; im->bucket_i <= im->tm->bucket_mask; im->bucket_i++) {
- struct list *bucket = &im->tm->buckets[im->bucket_i];
- if (!list_is_empty(bucket)) {
- return CONTAINER_OF(bucket, struct sw_flow, node);
- }
- }
- return NULL;
-}
-
-static int table_mac_iterator(struct sw_table *swt,
- struct swt_iterator *swt_iter)
-{
- struct swt_iterator_mac *im;
-
- swt_iter->private = im = malloc(sizeof *im);
- if (im == NULL)
- return 0;
-
- im->tm = (struct sw_table_mac *) swt;
-
- if (!im->tm->n_flows)
- swt_iter->flow = NULL;
- else {
- im->bucket_i = 0;
- swt_iter->flow = next_head_flow(im);
- }
-
- return 1;
-}
-
-static void table_mac_next(struct swt_iterator *swt_iter)
-{
- struct swt_iterator_mac *im;
- struct list *next;
-
- if (swt_iter->flow == NULL)
- return;
-
- im = (struct swt_iterator_mac *) swt_iter->private;
-
- next = swt_iter->flow->node.next;
- if (next != NULL) {
- swt_iter->flow = CONTAINER_OF(next, struct sw_flow, node);
- } else {
- im->bucket_i++;
- swt_iter->flow = next_head_flow(im);
- }
-}
-
-static void table_mac_iterator_destroy(struct swt_iterator *swt_iter)
-{
- free(swt_iter->private);
-}
-
-static void table_mac_stats(struct sw_table *swt, struct sw_table_stats *stats)
-{
- struct sw_table_mac *tm = (struct sw_table_mac *) swt;
- stats->name = "mac";
- stats->n_flows = tm->n_flows;
- stats->max_flows = tm->max_flows;
-}
-
-struct sw_table *table_mac_create(unsigned int n_buckets,
- unsigned int max_flows)
-{
- struct sw_table_mac *tm;
- struct sw_table *swt;
- unsigned int i;
-
- tm = calloc(1, sizeof *tm);
- if (tm == NULL)
- return NULL;
-
- assert(!(n_buckets & (n_buckets - 1)));
-
- tm->buckets = malloc(n_buckets * sizeof *tm->buckets);
- if (tm->buckets == NULL) {
- printf("failed to allocate %u buckets\n", n_buckets);
- free(tm);
- return NULL;
- }
- for (i = 0; i < n_buckets; i++) {
- list_init(&tm->buckets[i]);
- }
- tm->bucket_mask = n_buckets - 1;
-
- swt = &tm->swt;
- swt->lookup = table_mac_lookup;
- swt->insert = table_mac_insert;
- swt->delete = table_mac_delete;
- swt->timeout = table_mac_timeout;
- swt->destroy = table_mac_destroy;
- swt->stats = table_mac_stats;
-
- swt->iterator = table_mac_iterator;
- swt->iterator_next = table_mac_next;
- swt->iterator_destroy = table_mac_iterator_destroy;
-
- crc32_init(&tm->crc32, 0x04C11DB7); /* Ethernet CRC. */
- tm->n_flows = 0;
- tm->max_flows = max_flows;
-
- return swt;
-}
struct sw_flow_key;
struct list;
-/* Iterator through the flows stored in a table. */
-struct swt_iterator {
- struct sw_flow *flow; /* Current flow, for use by client. */
- void *private;
-};
-
/* Table statistics. */
struct sw_table_stats {
const char *name; /* Human-readable name. */
unsigned long int max_flows; /* Flow capacity. */
};
+/* Position within an iteration of a sw_table.
+ *
+ * The contents are private to the table implementation, except that a position
+ * initialized to all-zero-bits represents the start of a table. */
+struct sw_table_position {
+ unsigned long private[4];
+};
+
/* A single table of flows. */
struct sw_table {
/* Searches 'table' for a flow matching 'key', which must not have any
/* Destroys 'table', which must not have any users. */
void (*destroy)(struct sw_table *table);
- int (*iterator)(struct sw_table *, struct swt_iterator *);
- void (*iterator_next)(struct swt_iterator *);
- void (*iterator_destroy)(struct swt_iterator *);
+ /* Iterates through the flow entries in 'table', passing each one
+ * matches 'key' to 'callback'. The callback function should return 0
+ * to continue iteration or a nonzero error code to stop. The iterator
+ * function returns either 0 if the table iteration completed or the
+ * value returned by the callback function otherwise.
+ *
+ * The iteration starts at 'position', which may be initialized to
+ * all-zero-bits to iterate from the beginning of the table. If the
+ * iteration terminates due to an error from the callback function,
+ * 'position' is updated to a value that can be passed back to the
+ * iterator function to resume iteration later with the following
+ * flow. */
+ int (*iterate)(struct sw_table *table,
+ const struct sw_flow_key *key,
+ struct sw_table_position *position,
+ int (*callback)(struct sw_flow *flow, void *private),
+ void *private);
/* Dumps statistics for 'table' into 'stats'. */
void (*stats)(struct sw_table *table, struct sw_table_stats *stats);
};
-struct sw_table *table_mac_create(unsigned int n_buckets,
- unsigned int max_flows);
struct sw_table *table_hash_create(unsigned int polynomial,
unsigned int n_buckets);
struct sw_table *table_hash2_create(unsigned int poly0, unsigned int buckets0,
return oh;
}
+static void *
+alloc_stats_request(size_t body_len, uint16_t type, struct buffer **bufferp)
+{
+ struct ofp_stats_request *rq;
+ rq = alloc_openflow_buffer((offsetof(struct ofp_stats_request, body)
+ + body_len), OFPT_STATS_REQUEST, bufferp);
+ rq->type = htons(type);
+ rq->flags = htons(0);
+ return rq->body;
+}
+
static void
send_openflow_buffer(struct vconn *vconn, struct buffer *buffer)
{
}
static void
-dump_transaction(const char *vconn_name, uint8_t request_type)
+dump_transaction(const char *vconn_name, struct buffer *request)
{
struct vconn *vconn;
- struct buffer *request, *reply;
+ struct buffer *reply;
run(vconn_open_block(vconn_name, &vconn), "connecting to %s", vconn_name);
- alloc_openflow_buffer(sizeof(struct ofp_header), request_type, &request);
reply = transact_openflow(vconn, request);
ofp_print(stdout, reply->data, reply->size, 1);
vconn_close(vconn);
}
+static void
+dump_trivial_transaction(const char *vconn_name, uint8_t request_type)
+{
+ struct buffer *request;
+ alloc_openflow_buffer(sizeof(struct ofp_header), request_type, &request);
+ dump_transaction(vconn_name, request);
+}
+
+static void
+dump_stats_transaction(const char *vconn_name, struct buffer *request)
+{
+ uint32_t send_xid = ((struct ofp_header *) request->data)->xid;
+ struct vconn *vconn;
+ bool done = false;
+
+ run(vconn_open_block(vconn_name, &vconn), "connecting to %s", vconn_name);
+ send_openflow_buffer(vconn, request);
+ while (!done) {
+ uint32_t recv_xid;
+ struct buffer *reply;
+
+ run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
+ recv_xid = ((struct ofp_header *) reply->data)->xid;
+ if (send_xid == recv_xid) {
+ struct ofp_stats_reply *osr;
+
+ ofp_print(stdout, reply->data, reply->size, 1);
+
+ osr = buffer_at(reply, 0, sizeof *osr);
+ done = !osr || !(ntohs(osr->flags) & OFPSF_REPLY_MORE);
+ } else {
+ VLOG_DBG("received reply with xid %08"PRIx32" "
+ "!= expected %08"PRIx32, recv_xid, send_xid);
+ }
+ buffer_delete(reply);
+ }
+ vconn_close(vconn);
+}
+
+static void
+dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type)
+{
+ struct buffer *request;
+ alloc_stats_request(0, stats_type, &request);
+ dump_stats_transaction(vconn_name, request);
+}
+
static void
do_show(int argc UNUSED, char *argv[])
{
- dump_transaction(argv[1], OFPT_FEATURES_REQUEST);
- dump_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
+ dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST);
+ dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
}
static void
do_dump_tables(int argc, char *argv[])
{
- dump_transaction(argv[1], OFPT_TABLE_STATS_REQUEST);
+ dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
}
static void do_dump_flows(int argc, char *argv[])
{
- struct vconn *vconn;
- struct buffer *request;
struct ofp_flow_stats_request *req;
- uint32_t send_xid;
- bool done = false;
+ struct buffer *request;
- run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]);
- req = alloc_openflow_buffer(sizeof *req, OFPT_FLOW_STATS_REQUEST,
- &request);
+ req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, &req->table_id,
NULL);
req->type = OFPFS_INDIV;
req->pad = 0;
- send_xid = ((struct ofp_header *) request->data)->xid;
- send_openflow_buffer(vconn, request);
- while (!done) {
- uint32_t recv_xid;
- struct buffer *reply;
-
- run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
- recv_xid = ((struct ofp_header *) reply->data)->xid;
- if (send_xid == recv_xid) {
- struct ofp_flow_stats_reply *rpy;
-
- ofp_print(stdout, reply->data, reply->size, 1);
-
- rpy = buffer_at(reply, 0, sizeof *rpy);
- done = (!rpy
- || rpy->header.version != OFP_VERSION
- || rpy->header.type != OFPT_FLOW_STATS_REPLY
- || (ntohs(rpy->header.length)
- < sizeof rpy->header + sizeof *rpy->flows));
- } else {
- VLOG_DBG("received reply with xid %08"PRIx32" "
- "!= expected %08"PRIx32, recv_xid, send_xid);
- }
- buffer_delete(reply);
- }
- vconn_close(vconn);
+ dump_stats_transaction(argv[1], request);
}
static void do_add_flows(int argc, char *argv[])
static void
do_dump_ports(int argc, char *argv[])
{
- dump_transaction(argv[1], OFPT_PORT_STATS_REQUEST);
+ dump_trivial_stats_transaction(argv[1], OFPST_PORT);
}
static void do_help(int argc UNUSED, char *argv[] UNUSED)